.Net

Integrated Help system in WPF Application

Posted on December 9, 2012. Filed under: .Net, C#, WPF | Tags: , , , |


Introduction

I have WPF application and I am thinking of creating help documentation for it. If anyone needs help on some screen and press F1, I don’t like to open traditional CHM or HTML based screens for that. So my plan is that the screen will describe its own help description and Controls will give their introduction and also show you the activity flow make user understand about basic flow the screen. But what if User also wants to introduction of each control click while working on a screen? Ok, then I will also print the basic introduction into the below status bar on mouse click of control (If it have any). It is just quick guideline to get rid of reading long and boring documentation and give you very basic information of a screen.

Still not clear to you?!! No problem. Let us take a simple screen to understand my thinking here:

A simple Login screen which controls:

  1. Textbox for User name
  2. Password box
  3. Login button
  4. And a button to open Wish-List Child Screen

Let us talk on flow first. The First Three controls have some flow to show. What I mean is that,

  1. User need to set username first
  2. Then enter the password
  3. And then click on login button

Concept

dhelp

So here you can see, I have also added a status bar to show the control description while selecting control (Here in picture login button has been selected). But if user asks for help for this screen by clicking F1, the screen should be look like this –

dhelp-hmode

To do this I have prepared documentation XML for this, Where I have kept all the necessary descriptions of a control like Title, Help Description, URL for see online Guide, Shot Cut Key/Hot Key and Flow Index (If Any) for ordering the activity flow sequences. You do need to synchronize the xml by yourself you have changes any workflow or short-cut/hot keys which is true for other documentation also. It off-course not a replacement of documentation guide, I just quick guideline for user. That is why I have kept an URL here to go to on-line guide for more details.

xml

Here I have put the element name as unique by I am going to map this documentation with the control use in UI. Flow Indexes has also been set. If any control which is not a part of some flow, I mean User can use it whenever he want e.g. search control, launching child window for settings or sending wish-list simply keep the flow Index empty.

No flow

And Result will be look something like this

Untitled

Using the code

To load and read this xml, I have prepared a Loader class which load and generate Dynamic Help data model based on chosen a language. I am maintaining multiple XMLs with same just like a Resource file do. In this sample, during the initialization of application I do xml loading and model generation in memory to cache. Later on I am going use these help definition based on some UI work.

   public class DynamicHelpStringLoader
    {
        private const string HelpStringReferenceFolder = "DynamicHelpReference";
        private const string UsFileName = "DynamicHelp_EN_US.xml";
        private const string FrFileName = "DynamicHelp_FR.xml";
        private const string EsFileName = "DynamicHelp_ES.xml";
        private const string DefaultFileName = "DynamicHelp_EN_US.xml";
 
        /// <summary>
        /// This is the collection where all the JerichoMessage objects
        /// will be stored.
        /// </summary>
        private static readonly Dictionary<string, DynamicHelpModel> HelpMessages;
 
        private static Languages _languageType;
 
        /// <summary>
        /// The static constructor.
        /// </summary>
        static DynamicHelpStringLoader()
        {
            HelpMessages = new Dictionary<string,DynamicHelpModel>();
            _languageType = Languages.None;
        }
        /// <summary>
        /// Generates the collection of JerichoMessage objects as if the provided language.
        /// </summary>
        /// <param name="languages">The Languages enum. Represents the user's choice of language.</param>
        public static void GenerateCollection(Languages languages)
        {
            if (_languageType == languages)
            {
                return;
            }
            _languageType = languages;
            string startUpPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly()
.GetModules()[0].FullyQualifiedName);
            string fileName;
            switch (languages)
            {
                case Languages.English:
                    fileName = UsFileName;
                    break;
                case Languages.French:
                    fileName = FrFileName;
                    break;
                case Languages.Spanish:
                    fileName = EsFileName;
                    break;
                default:
                    fileName = DefaultFileName;
                    break;
            }
 
            Task.Factory.StartNew(() =>
                                      {
                                          LoadXmlFile(Path.Combine(startUpPath,
                                                                   string.Format(@"{0}\{1}", HelpStringReferenceFolder,
                                                                                 fileName)));
                                      });
        }
        /// <summary>
        /// Load the provided xml file and populate the dictionary.
        /// </summary>
        /// <param name="fileName"></param>
        private static void LoadXmlFile(string fileName)
        {
            XDocument doc = null;
            try
            {
                //Load the XML Document                
                doc = XDocument.Load(fileName);
                //clear the dictionary
                HelpMessages.Clear();
 
                var helpCodeTypes = doc.Descendants("item");
                //now, populate the collection with JerichoMessage objects
                foreach (XElement message in helpCodeTypes)
                {
                    var key = message.Attribute("element_name").Value;
                    if(!string.IsNullOrWhiteSpace(key))
                    {
                        var index = 0;
                        //get all Message elements under the help type
                        //create a JerichoMessage object and insert appropriate values
                        var dynamicHelp = new DynamicHelpModel
                                              {
                                                  Title = message.Element("title").Value,
                                                  HelpText = message.Element("helptext").Value,
                                                  URL = message.Element("moreURL").Value,
                                                  ShortCut = message.Element("shortcut").Value,
                                                  FlowIndex = (int.TryParse(message.Element("flowindex").Value, out index)) ? index : 0
                                              };
                        //add the JerichoMessage into the collection
                        HelpMessages.Add(key.TrimStart().TrimEnd(), dynamicHelp);
                    }
                }
 
            }
            catch (FileNotFoundException)
            {
                throw new Exception(LanguageLoader.GetText("HelpCodeFileNotFound"));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        /// <summary>
        /// Returns mathced string from the xml.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static DynamicHelpModel GetDynamicHelp(string name)
        {
            
            if(!string.IsNullOrWhiteSpace(name))
            {
                var key = name.TrimStart().TrimEnd();
                if(HelpMessages.ContainsKey(key))
                    return HelpMessages[key];
            }
            return new DynamicHelpModel();
        }
 

    } 

Now it is time to jump into UI work. I have created a attach property which enable dynamic help for a screen or window. So It will be simple Boolean attach property. On setting it, I am creating Help Group and add into a list. This list is necessary while working with child windows. The help group keeping is the element which has been enable as dynamic help. It is normally the root panel of a window .In sample, I have used the first child panel of window as element to dynamic help enable to get Adornerlayer where I can set the Text – “Help Model (Press F1 again to Exit)”.

You could look into Xaml here where I have put the element name and later on these unique string going to be mapped for retrieving help description.

xamluse

I also have kept the window and hooked closing event and mouse click and bind Command of ApplicationCommands.Help. Mouse Click event has been subscribed to get the find out the control it currently in and check for Help description to in status bar. Help command has been bound to get F1 pressed and toggle the help mode. In help mode I am going to find out all the controls with help description in the children of the element where you have setup the attached property. I need to hook the closing event here to clearing the Help Group with all its event-subscriptions.

        private static bool HelpActive { get; set; }
 
        public static void SetDynamicHelp(UIElement element, bool value)
        {
            element.SetValue(DynamicHelpProperty, value);
        }
        public static bool GetDynamicHelp(UIElement element)
        {
            return (Boolean)element.GetValue(DynamicHelpProperty);
        }
 
        public static readonly DependencyProperty DynamicHelpProperty =
          DependencyProperty.RegisterAttached("DynamicHelp", typeof(bool), typeof(UIElement),
                                              new PropertyMetadata(false, DynamicHelpChanged));
 
        private static readonly List<HelpGroup> HelpGroups = new List<HelpGroup>();
 
        public static HelpGroup Current
        {
            get
            {
                return HelpGroups.LastOrDefault();
            }
        }
 
        private static void DynamicHelpChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as UIElement;
 
            if (null != element)
            {
                if (null != HelpGroups && !HelpGroups.Any(g => null != g.Element && g.Element.Equals(element)))
                {
                    UIElement window = null;
                    if (element is Window)
                        window = (Window)element;
                    else
                        window = Window.GetWindow(element);
 
                    //Note: Use below code if you have used any custom window class other than 
                    //child of Window (for example WindowBase is base of your custom window)
                    //if (window == null)
                    //{
                    //    if (element is WindowBase)
                    //        window = (WindowBase)element;
                    //    else
                    //        window = element.TryFindParent<WindowBase>();
                    //}

                    if (null != window)
                    {
                        var currentGroup = new HelpGroup { Screen = window, Element = element, ScreenAdorner = 
new HelpTextAdorner(element) };
                        var newVal = (bool)e.NewValue;
                        var oldVal = (bool)e.OldValue;
                                              
                        // Register Events
                        if (newVal && !oldVal)
                        {
                            if (currentGroup.Screen != null)
                            {
                                if (!currentGroup.Screen.CommandBindings.OfType<CommandBinding>()
.Any(c => c.Command.Equals(ApplicationCommands.Help)))
                                {
                                    if (currentGroup._helpCommandBind == null)
                                    {
                                        currentGroup._helpCommandBind = 
new CommandBinding(ApplicationCommands.Help, HelpCommandExecute);
                                    }
                                    currentGroup.Screen.CommandBindings.Add(currentGroup._helpCommandBind);
                                }
 
                                if (currentGroup._helpHandler == null)
                                {
                                    currentGroup._helpHandler = new MouseButtonEventHandler(ElementMouse);
                                }
                                currentGroup.Screen.PreviewMouseLeftButtonDown += currentGroup._helpHandler;
                                if (window is Window)
                                    ((Window)currentGroup.Screen).Closing += WindowClosing;
                                //else
                                //((WindowBase)currentGroup.Screen).Closed += new EventHandler<WindowClosedEventArgs>(RadWindowClosed);
                            }
                        }
                        HelpGroups.Add(currentGroup);
                    }
                }
            }
 
        }
 

Lets come to mouse click event and how do I find the control with help description. Here it traverses to the top until I got the control with help description. On finding the control it will be able to show you the description in below status bar.

Here in this method ElementMouse a his test has been executed using InputHitTest to get control user clicked.  After that it check for help description, if not found it goes to parent and check. So I am traversing here to the top until I didn’t found nearer control with help description. 

static void ElementMouse(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if(e.ButtonState != MouseButtonState.Pressed
                || e.ClickCount != 1)
                return;
 
            var element = sender as DependencyObject;
            if (null != element)
            {
                UIElement window = null;
                if (element is Window)
                    window = (Window)element;
                else
                    window = Window.GetWindow(element);
 
                //Note:  Use bellow code if you have used any custom window class other than child 
                //of Window (for example WindowBase is base of your custom window)
                //if (window == null)
                //{
                //    if (element is WindowBase)
                //        window = (WindowBase) element;
                //    else
                //        window = element.TryFindParent<WindowBase>();
                //}

                if (null != window)
                {
                    // Walk up the tree in case a parent element has help defined
                    var hitElement = (DependencyObject)window.InputHitTest(e.GetPosition(window));
 
                    var checkHelpDo = hitElement;                    
                    string helpText = Current.FetchHelpText(checkHelpDo);
                    while ( string.IsNullOrWhiteSpace(helpText) && checkHelpDo != null &&
                            !Equals(checkHelpDo, Current.Element) &&
                            !Equals(checkHelpDo, window))
                    {
                        checkHelpDo = (checkHelpDo is Visual)?  VisualTreeHelper.GetParent(checkHelpDo) : null;
                        helpText = Current.FetchHelpText(checkHelpDo);
                    }
                    if (string.IsNullOrWhiteSpace(helpText))
                    {
                        Current.HelpDO = null;
                    }
                    else if (!string.IsNullOrWhiteSpace(helpText) && Current.HelpDO != checkHelpDo)
                    {
                        Current.HelpDO = checkHelpDo;
                    }
 
                    if (null != OnHelpMessagePublished)
                       OnHelpMessagePublished(checkHelpDo, new HelperPublishEventArgs() { HelpMessage = helpText, Sender = hitElement});
                    
                }
            }
        }

On the help command execution it toggles the help mode. If help mode is true, then I have traversed the children recursively to find out all children with Help description and start a timer there to show popup on those controls in a sequential way.

        private static void DoGenerateHelpControl(DependencyObject dependObj, HelperModeEventArgs e)
        {            
            // Continue recursive toggle. Using the VisualTreeHelper works nicely.
            for (int x = 0; x < VisualTreeHelper.GetChildrenCount(dependObj); x++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dependObj, x);
                DoGenerateHelpControl(child, e);
            }
 
            // BitmapEffect is defined on UIElement so our DependencyObject 
            // must be a UIElement also
            if (dependObj is UIElement)
            {
                var element = (UIElement)dependObj;
                if (e.IsHelpActive)
                {
                    var helpText = e.Current.FetchHelpText(element);
                    if (!string.IsNullOrWhiteSpace(helpText) && element.IsVisible
                        && !IsWindowAdornerItem(element))
                    {
                        // Any effect can be used, I chose a simple yellow highlight
                        _helpElements.Add(new HelpElementArgs() { Element = element, 
                            HelpData = DynamicHelperViewer.GetPopUpTemplate(element, helpText, e.Current), 
                            Group = e.Current });
                    }
                }
                else if (element.Effect == HelpGlow)
                {
                    if(null != OnHelpTextCollaped)
                        OnHelpTextCollaped(null, new HelpElementArgs(){ Element =element, Group = e.Current});
                }
            }
        }   

Controls those don’t have any flow has been shown of first tick of timer. After that flow text has been shown with their flow index in sequential way. For this, I have found out the minimum flow index and then found out the data according to that index and show their pop up. I have also removed those from list since those has already been shown.

 public static void HelpTimerTick(object sender, ElapsedEventArgs args)
        {
            if(null != _helpElements && _helpElements.Count > 0)
            {
                int idx = _helpElements.Min(e => e.HelpData.Data.FlowIndex);
                var data = _helpElements.Where(e => e.HelpData.Data.FlowIndex.Equals(idx));
                foreach (var helpElementArgse in data.ToList())
                {
                    _helpElements.Remove(helpElementArgse);
                    if (null != OnHelpTextShown)
                    {
                        OnHelpTextShown(sender, helpElementArgse);
                    }   
                }                
            }
            else
            {
                _helpTimer.Enabled = false;
            }
        }  

For the child window, it going to give you same kind of result if you set enable to dynamic help for them.

dhelp-hmode CHILD

Points of Interest

To show the popup, I have used adorners as popup to get the relocate feature with controls on resize the screen and it will not be topmost of all application. But this pop up failed be on top of screen if I set my control near the boundaries of the screen. The result will occur cut off the popup since of unavailable space to show. I could use popup here with changing the topmost behaviour. I am trying to provide a concept here which doesn’t meet all the expectations or features we are expecting from documentation or help guide. But I think it give user quick guideline to understand a workflow of screen without reading a boring(!!) documentation guide.  So it is not the replacement of long documentation. You can keep both and allow user to open your long documentation from this quick documentation.

References

Read Full Post | Make a Comment ( None so far )

Getting performance benefit from DbContext[EF 5] in Entity Framework

Posted on November 1, 2012. Filed under: .Net, C#, Code-First, dbcontext, ef5, Entity Framework | Tags: , , , , , |


I did talk about the distributed architecture here with Entity framework code-first.

Now I would like to reminds you about the performance benefit features come with DBContext and it will also help you to extend your repositories and queries. Here is the followings:

No Tracking and Auto-Compiled Queries: 

In my early version of repository pattern, I have used ObjectQuery to get the features  of MargeOption and EnablePlanCaching properties.  

Setting MergeOption to NoTracking has helped us to take advantage of the EF ability to return data that doesn’t need to be tracked by the context. Say I don’t have any plan to make changes to that data. Therefore, I like to avoid the performance hit taken when EF creates ObjectStateEntry instances for each object it’s tracking, as well as forcing the context to be aware of any changes made to those objects.  EF Team has realized needs here and they have provided you an extension method of IQueryable - AsNoTracking()[Where(expression).AsNoTracking()].

Settting EnablePlanCaching to true has been used for enabling the caching plan first time it has been executed and next there was no more complexity for creating TSQL from LinqToEntities. You could also achieve this by using CompiledQuery Class to re-use precompiled query. Unfortunately there is no way to use CompiledQuery in DbContext API. CompiledQuery works only with ObjectContext. If we are using Code First, we are using the DbContext API. EF team has provided auto-compiled queries, which work very differently than CompiledQuery. Instead of your writing code to compile each query and then invoking each as needed, Entity Framework caches the generated SQL for you as a background process, then searches the cache for already compiled queries when you execute any query. It solves many issues of pre-compiled queries and gives us flexibility to cached 800 queries Item. And most wonderful news is you don’t need to write any code or project conversion to 4.5 to achieve this. All you need is .NET 4.5 runtime in your machine.

143875_fig1_auto-compiled_query_ef5

Find Method:

As you know, Find method is providing you to find object with its key and there is also performance benefit lies here. The Find method will first look in memory for a matching object that is being tracked by the context. If that is found in memory, then Entity Framework won’t bother querying the database. So, no more visit to database and mapping complexity if it is already there my memory.

Turn off Detect Changes and Validation:

I have found DBContext more organized and friendly in place of ObjectContext. Now user has the full flexibility to turn off and turn on the calling of change detection and validation. You can find this under the Property Configuration. Change tracking and the DetectChanges method are a part of the stack where there is a penalty of performance. For this reason it can be useful to have an idea of what is actually going on such you can make an informed decision on what to do if the default behaviour is not right for your application. If your context is not tracking a large number of entities you pretty much never need to switch off automatic DetectChanges otherwise I will suggest you turned it off and call DetectChanges where it seems necessary.

First Level Caching

There are also some features like Local property which provide you the first level in-memory caching objects available in state entries without the deleted marked objects and you can get the benefit if you want during the life time of a Context instance.

OK, that is it. Thanks for reading.

References

Read Full Post | Make a Comment ( None so far )

Implementing Undo/Redo feature for DbContext of Entity Framework Code-First

Posted on October 11, 2012. Filed under: .Net, C#, Code-First, EF4, Entity Framework, WCF | Tags: , , , , , |


Download Source Code Here: Source Code

I was curious about change tracking and property values of DbContext and  while playing with that I decided to write snapshot manager which would  take snapshot of my each changes by storing entries And this manager also allow me to undo and redo my changes with DbContext.

DbContext will check all the entities of all types when change tracking is enabled and also verify if they have any changes in their data. Automatic Change tracking is enabled by default. Disabling it would not trigger the DbContext update for each change in the entity. Actually it maintains the state of entities. It uses this to determine the changes needed to be pushed to database when SaveChanges() is called.

The Undo and Redo features allow you to easily correct mistakes or based on some scenario, as well as free you to experiment with different routing and data mapping decisions. Undo reverses the last action you performed, and Redo undoes the last Undo action.

So let’s come to point of creating our snapshots of changes. Since DbContext still doesn’t give any event of DbContext.DetectChanges()execution, I have decided to keep my snapshot in my Repository’s CUD( Create , Update , Delete) operations and also allow user to call it explicitly whenever it is needed- Click here to read my Article in Codeproject about this topic.

Read Full Post | Make a Comment ( None so far )

Repository pattern with Entity Framework Code-First in Composable Service End

Posted on October 3, 2012. Filed under: .Net, C#, EF4, Entity Framework, Expression, WCF | Tags: , , , , , , , , |


Download Source Here

Introduction

Some days ago I was thinking to design data access layer in Composable service end and also decide to use Entity Framework Code-First approach since I didn’t tried that yet after it has been released. That’s why I plan some interfaces act as a contract between composable parts. So client can create proxy by Export and Import using MEF. Beside the service contract I plan to do same job by using some interfaces I need to provide that data and wish avoid coupling and dependency and here the following interfaces in service implementation – IUnitOFWork ,IRepository and IContext to separate the logic that retrieves the data and maps it to the entity model from the business logic that acts on the model. Here are following reason I choose to go in Composable environment –

  • Removes the need for a component to locate its dependencies or manage their lifetimes.
  • Allows swapping of implemented dependencies without affecting the component.
  • Facilitates testability by allowing dependencies to be mocked.
  • Increases maintainability by allowing new components to be easily added to the system.

   And Here is some reason for using repository pattern in data access layer in place direct access the database code-

  • Duplicated code
  • A higher potential for programming errors
  • Weak typing of the business data
  • Difficulty in centralizing data-related policies such as caching
  • An inability to easily test the business logic in isolation from external dependencies

Environment

Model 

I have chosen a very small environment where I just have two model of BlogPost and Category and also a Audit Model which represent the audit log generated by the system when any changes has been made in database.

model c

Data Access  

Data Access is going to hold three interfaces as I have mentioned earlier  – IRepository, IUnitOfWork and    IContext  and their implementation with DbContext and DbSet. Here I have DbContext named BlogContext and their Initializer.

data access c

Using the code  

So here we are going define our Composable Service first. To do that we need to extend service behavior and instance provider there. This instance provider will use a InstanceContext extension named ServiceComposer while creating and releasing instance.

 public class ComposingInstanceProvider : IInstanceProvider
    {
        private Type serviceType;
 
        public ComposingInstanceProvider(Type serviceType)
        {
            this.serviceType = serviceType;
        }
 
        public object GetInstance(System.ServiceModel.InstanceContext instanceContext)
        {
            return this.GetInstance(instanceContext, null);
        }
 
        public object GetInstance(System.ServiceModel.InstanceContext instanceContext,
 System.ServiceModel.Channels.Message message)
        {
            // Create composer:
            var composer = new ServiceComposer();
            instanceContext.Extensions.Add(composer);
 
            // Retrieve instance:
            var instance = Activator.CreateInstance(this.serviceType);
 
            // Compose instance:
            composer.Compose(instance);
 
            // Return instance:
            return instance;
        }
 
        public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance)
        {
            // Remove composer:
            var composer = instanceContext.Extensions.Find<ServiceComposer>();
            if (composer != null)
                instanceContext.Extensions.Remove(composer);
 
            // Release instance:
            if (instance is IDisposable)
                ((IDisposable)instance).Dispose();
        }
    }   

In  GetInstance method, a composer name ServiceComposer has been created and add as extension of InstanceContext. Lets take deeper look into that extention class and you can see that the composition part of our service reside in this ServiceComposer class. we have used CompositionContainer and register the AggregateCatalog and container itself. Before you can inject dependencies into an object, the types of the dependencies need to be registered with the container. Registering a type typically involves passing the container an interface and a concrete type that implements that interface. There are primarily two means for registering types and objects: through code or through configuration. Here in this solution We will register types or a mapping with the container. At the appropriate time, the container will build an instance of the type you specify.

        void IExtension<InstanceContext>.Attach(InstanceContext owner)
        {
            compositionContainer = new CompositionContainer(Settings.DefaultCatalog);
            compositionContainer.ComposeExportedValue(compositionContainer);
        }
 
        void IExtension<InstanceContext>.Detach(InstanceContext owner)
        {
            if (compositionContainer != null)
            {
                compositionContainer.Dispose();
                compositionContainer = null;
            }
        } 

Service composer c

So after using the above ServiceBehaviour in service implementation, we will get into composable from the client end we can do Export my service like this way :

    [Export]
    public class LogicServiceClient : ClientBase<ILogicService>
    {
        public virtual ILogicService Invoke
        {
            get { return this.Channel; }
        }
    }    

So now lets jump to the implementation of ILogicService and the data access part to create Repository, UnitOfWork and DbContext. In the implementation of service end, we would like to write code for access data with interfaces of these stuff. So we are hoping to code for data access will be look something like this –

        [Import]
        Lazy<IUnitOfWork> _unitOfWork;
 

        public bool SetData( BlogPost post)
        {
            using (var db = _unitOfWork.Value)
            {
                db.EnableAuditLog = false; 
                using (var transaction = db.BeginTransaction())
                {
                    try
                    {
                        IRepository<BlogPost> repository = db.GetRepository<BlogPost>();
                        repository.Add(post);
                        int i = db.Commit();
                        transaction.Commit();
                        return (i > 0);
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                        throw;
                    }
                }
            }
            return false;
        }  

We have used Lazy instance creation of UnitOfWork here. So lets define our first interface of IUnitOfWork

    public interface IUnitOfWork: IDisposable 
    {
        bool EnableAuditLog { get; set; }     
        int Commit();
        IRepository<TSet> GetRepository<TSet>() where TSet : class;
        DbTransaction BeginTransaction();
    }   

Here this UnitOfWork is responsible to creating transaction, commit the changes made , switch on/off audit logging and a factory-method for creating repository instances of expected entity. Here is its implementation

    [Export(typeof(IUnitOfWork))]
    public class UnitOfWork : IUnitOfWork
    {
        [Import]
        private Lazy<IContext> _context;
 
        private CompositionContainer _container;
        private DbTransaction _transaction;
        private Dictionary<Type, object> _repositories;
 
        public bool EnableAuditLog
        {
            get{ return _context.Value.IsAuditEnabled; }
            set { _context.Value.IsAuditEnabled = value; }
        }
 
        [ImportingConstructor]
        public UnitOfWork(CompositionContainer container)
        {
            _container = container;
            _repositories = new Dictionary<Type, object>();
 
        }
 
        public IRepository<TSet> GetRepository<TSet>() where TSet : class
        {
            if (_repositories.Keys.Contains(typeof(TSet)))
                return _repositories[typeof(TSet)] as IRepository<TSet>;
 
            var repository =  _container.GetExportedValue<IRepository<TSet>>();
            repository.Context = this._context.Value;
            _repositories.Add(typeof(TSet), repository);
            return repository;
        }
 
        public void Dispose()
        {
            if (null != _transaction)
                _transaction.Dispose();
 
            if (null != _context.Value)
                _context.Value.Dispose();
        }
       
        public int Commit()
        {
           return _context.Value.Commit();
        }
 
        public DbTransaction BeginTransaction()
        {
           _transaction = _context.Value.BeginTransaction();
           return _transaction;
        }
    } 

In UnitOfWork , feature like commit, transaction and audit option are handled by IContext which is generally represent DbContext and we will see it’s implementation later below. GetRepository() method is going to create IRepository of entity-set and keeping those object reference into Dictionary in reason to re-use  if anyone want it later.The UnitOfWork implements the IDisposable interface to dispose and release all the resources of the DbContext instances also with Transection object Database Connection. The Dispose() method will be automatically called because of the using construct. It is called once scope of the UnitOfWork terminates.

Now, lets concentrate into our IRepository interface. Repositories help us with code reusability and improve testability because they implement interfaces that can be mocked using various mocking frameworks. If all you need is CRUD operations for your new repository then besides writing the name of new class and interface you don’t need to write anything else because your domain specific repository will be using composition approach-

Now, lets concentrate into our IRepository interface-

    public interface IRepository<T> 
    {
        IContext Context { get; set; }
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        T FindSingle(Expression<Func<T, bool>> predicate = null,
        params Expression<Func<T, object>>[] includes);
        IQueryable<T> Find(Expression<Func<T, bool>> predicate = null,
        params Expression<Func<T, object>>[] includes);
        IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties);
        int Count(Expression<Func<T, bool>> predicate = null);
        bool Exist(Expression<Func<T, bool>> predicate = null);
    }  

All those stuff related to entity-set has been kept here like Create, Update and Delete and Queries.There are two ways that the repository can query business entities. It can submit a query object to the client’s business logic or it can use methods that specify the business criteria. In the latter case, the repository forms the query on the client’s behalf. The repository returns a matching set of entities that satisfy the query. Now look into the Repository

    [Export(typeof(IRepository<>))]
    public class Repository<T> : IRepository<T> where T : class
    {
        public IContext Context
        {
            get;
            set;
        }
 
        public void Add(T entity)
        {
            this.Context.GetEntitySet<T>().Add(entity);
        }
 
        public void Update(T entity)
        {
            this.Context.ChangeState(entity, System.Data.EntityState.Modified);
        }
        
        public T FindSingle(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null,
 params System.Linq.Expressions.Expression<Func<T, object>>[] includes)
        {
            var set = FindIncluding(includes);
            return (predicate == null) ?
                   set.FirstOrDefault() :
                   set.FirstOrDefault(predicate);
        }
 
        public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null,
 params System.Linq.Expressions.Expression<Func<T, object>>[] includes)
        {
            var set = FindIncluding(includes);
            return (predicate == null) ? set : set.Where(predicate);
        }
 
        public IQueryable<T> FindIncluding(params System.Linq.Expressions.Expression<Func<T, 
object>>[] includeProperties)
        {
            var set = this.Context.GetEntitySet<T>();
 
            if (includeProperties != null)
            {
                foreach (var include in includeProperties)
                {
                    set.Include(include);
                }
            }
            return set.AsQueryable();
        }
   
        public int Count(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null)
        {
            var set = this.Context.GetEntitySet<T>();
            return (predicate == null) ?
                   set.Count() :
                   set.Count(predicate);
        }
 
        public bool Exist(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null)
        {
            var set = this.Context.GetEntitySet<T>();
            return (predicate == null) ? set.Any() : set.Any(predicate);
        }
 

        public void Remove(T entity)
        {
            this.Context.ChangeState(entity, System.Data.EntityState.Deleted);
        }
    }

you can see that in most of the method, IContext instance is providing the entity-set of type- IDbSet by the flowing method –

 this.Context.GetEntitySet<T>();  

and during Remove and Update it call method to change state of the entry. So here full look of IContext interface-

public interface IContext : IDisposable
    {
        bool IsAuditEnabled { get; set; }
        IDbSet<T> GetEntitySet<T>() where T : class;
        void ChangeState<T>(T entity, EntityState state) where T : class;
        DbTransaction BeginTransaction();
        int Commit();
    }     

Both UnitOfWork and Repository has used this IContext which is originally provide some services of DbContext. To avoid dependency of  DbContext , IContext has been introduced which provide aggregate root in Repository and UnitOfWork. Here is the DbContext implementation:

[Export(typeof(IContext))]
    public class BlogContext : DbContext, IContext
    {
        public BlogContext()
            : base()
        {
            IsAuditEnabled = true;
            ObjectContext.SavingChanges += OnSavingChanges;
        }
 
        public ObjectContext ObjectContext
        {
            get
            {
                return (this as IObjectContextAdapter).ObjectContext;
            }
        }.....................................

Here we have access the ObjectContext and use it get the event before saving changes into the database. I think you can guess it has been used ..yes, right ….it is for generating the audit log based on the changes we have done. I will describe that implementation later . Let see the implemetation of IContext

        #region IContext Implementation
 
        public bool IsAuditEnabled
        {
            get;
            set;
        }
 
        public void ChangeState<T>(T entity, EntityState state) where T : class
        {
            Entry<T>(entity).State = state;
        }
 
        public IDbSet<T> GetEntitySet<T>()
        where T : class
        {
            return Set<T>();
        }
 
        public virtual int Commit()
        {
            if (this.ChangeTracker.Entries().Any(IsChanged))
            {
                return this.SaveChanges();
            }
            return 0;
        }
 
        private static bool IsChanged(DbEntityEntry entity)
        {
            return IsStateEqual(entity, EntityState.Added) ||
                   IsStateEqual(entity, EntityState.Deleted) ||
                   IsStateEqual(entity, EntityState.Modified);
        }
 
        private static bool IsStateEqual(DbEntityEntry entity, EntityState state)
        {
            return (entity.State & state) == state;
        }
 
        public virtual DbTransaction BeginTransaction()
        {
            var connection = (this as IObjectContextAdapter).ObjectContext.Connection;
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
 
            return connection
                .BeginTransaction(IsolationLevel.ReadCommitted);
        }
        #endregion  

GetEntitySet<T>() and  ChangeState<T>(T entity, EntityState state) is only two methods those currently is used Respository class to provide DbSet and changing state respectively and all others providing support for IUnitOfWork like creating transaction and commit changes into the database.

Now, To generate audit logs into the database through the following method:

      private List<Audit> CreateAuditRecordsForChanges(DbEntityEntry dbEntry)
        {
            List<Audit> result = new List<Audit>();
 
            #region Generate Audit
            //determine audit time
            DateTime auditTime = DateTime.UtcNow;
            
            // Get the Table name by attribute
            TableAttribute tableAttr = dbEntry.Entity.GetType()
.GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
            string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
 
            // Find Primiray key.
            string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => 
p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
 
            if (dbEntry.State == System.Data.EntityState.Added)
            {
                result.Add(new Audit()
                {
                    Id = Guid.NewGuid(),
                    AuditDateInUTC = auditTime,
                    AuditState = AuditState.Added,
                    TableName = tableName,
                    RecordID = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(),  
                    // Again, adjust this if you have a multi-column key
                    NewValue =ToXmlString( dbEntry.CurrentValues.ToObject())
                }
                    );
            }
            else if (dbEntry.State == System.Data.EntityState.Deleted)
            {
                result.Add(new Audit()
                {
                    Id = Guid.NewGuid(),
                    AuditDateInUTC = auditTime,
                    AuditState = AuditState.Deleted,
                    TableName = tableName,
                    RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
                    NewValue = ToXmlString( dbEntry.OriginalValues.ToObject().ToString())
                }
                    );
            }
            else if (dbEntry.State == System.Data.EntityState.Modified)
            {
                foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
                {
                    if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName)
, dbEntry.CurrentValues.GetValue<object>(propertyName)))
                    {
                        result.Add(new Audit()
                        {
                            Id = Guid.NewGuid(),
                            AuditDateInUTC = auditTime,
                            AuditState = AuditState.Added,
                            TableName = tableName,
                            RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
                            ColumnName = propertyName,
                            OriginalValue = dbEntry.OriginalValues.GetValue<object>(propertyName) == null ? 
                            null 
                            : dbEntry.OriginalValues.GetValue<object>(propertyName).ToString(),
                            
                            NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? 
                            null 
                            : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString()
                        }
                            );
                    }
                }
            }
            return result;
 
            #endregion 
        } 

During audit, DbEntityEntry has been used here. Table attribute on the model-

    [Table("Categories")]
    [DataContract]
    public class Category : BaseModel
    {.............  

has been seen to get table name and key attribute on field-

    [DataContract]
    public class BaseModel
    {
        [Key]
        [DataMember]
        public Guid Id { get; set;   .......... 

has been used to get primary key. If you have multiple primary key, then you need to handle  how to store them into database. Here in this example I assume single primary key for each model.

And From the entry Original and Current values has been used  to generate  audit  logs. Ok that is it. Thanks For reading.

References

Read Full Post | Make a Comment ( 5 so far )

Code Analyzing and NDepend

Posted on August 17, 2012. Filed under: .Net, Analyzing And Research, CodeProject, NDepend | Tags: , , |


In my childhood, my uncle has shown me how to see the cloud in a close look and I understand that one can draw some elements of the Earth in the sky-canvas if he/she wants to. After that the cloud becomes more close me and It teaches me one thing that, a deeper-look to something will give you some clues to draw your imagination. You can able to see that one which you have build-up in your mind.

Years past, after completing my graduation, I start my career as a software engineer but I have notice that I have not that much passion in my coding and development which I should be to enjoy my profession and I have started asking myself- am I doing any engineering here? Is my code becoming that thing which I have designed in my mind? So to find that answer I have tried that old solution here. I have decided to come closer to my code start analyzing them. And you know what, it is really working for me and at least it gives me the confidence that I can build something that I really want to. I can draw my thinking there through my code and can build-up my vision that I have designed in my mind. Yes, Code analyzing is an amazing thing. It helps you see your code quality, matrix, design, dependency, naming conversion, purity, visibility, architecture, layering and even the dead codes in your application.

I have started my first code analyzing tool for DOTNET with FXCOP which I have been introduced while I was reading a very nice book– Framework Design Guideline. After that I am hoping to more closer and freedom to go over my code analyzing. I got NDEPEND from Patrick Smacchia who has given me the opportunity to play with NDepend 4. Here I am picking the NDEPND to introduce a tool for analyzing you code.

I found some amazing things here in Ndepend :

Why Analyzing Code Structure, Design, Dependencies? It helps you

  • To avoid dependencies cycles between your components.
  • To know about layering and dependencies issues in your code base.
  • To prevent design erosion of your code base.
  • Care about fabricated complexity and how to reduce it effectively
  • Details the Level metric definition and usage.
  • Through Hints on how to componentize existing code
  • To Know Dependencies and Concerns
  • To Detect All Paths from A to B
  • To Re-factoring, Re-Structuring and the cost of Levelizing
  • Evolutionary Design and Acyclic componentization
  • Understanding Code: Static vs Dynamic Dependencies

Here is the dependency result for one of my Application:

all

Why Build Comparison? It will allow you –

  • To write rules that detects API breaking changes.
  • To focus code review on code that have been changed and added since the last release.
  • To Quality review on code that have been changed and added since a certain milestone.
  • To detect when new or refactored code is poorly covered by tests.

Ok now let me talk about the Code Metrics. In Ndepend I have found Code Metrics on:

– Application

– Assemblies (by measuring coupling between types of your application.)

– Namespaces

– Type

– Method

– Field

clip_image004

clip_image006

Metrics on Application gonna give you the following:

  • Lines of Code (NDepend computes this metric directly from the info provided in PDB files. The LOC for a method is equals to the number of sequence point found for this method in the PDB file. A sequence point is used to mark a spot in the IL code that corresponds to a specific location in the original source.)
  • Lines of Comments( Ndepend needs PDB files present and its corresponding source files)
  • Comment Percentage
  • Lines of code covered
  • Lines of code not covered
  • And all five things mention above ( Assemblies ,Namespaces, Type, methods and Fields)

Metrics on assemblies, Namespaces, Type, methods and Fields(only Afferent Coupling) allow you find two main coupling here on their respective level –

– Afferent Coupling
– Efferent coupling

Other things of Metrics on assemblies
– Relational Cohesion
– Instability
– Abstractness
– Distance from main sequence

Other things of Metrics on Namespaces
– Level(defined for assemblies, namespaces, types, methods)

Other things of Metrics on Type –
– Lack of Cohesion Of Methods
– Cyclomatic Complexity (defined for types, methods)
– IL Cyclomatic Complexity
– Size of instance (defined for instance fields and types)
– Interfaces Implemented
– Number of Children
– Depth of Inheritance Tree

Other things of Metrics on Methods
– IL Nesting Depth
– Parameters
– Variables
– Overloads
– Percentage Branch Coverage

Here is the Matrices result on one of current application:

clip_image008

What more:

Yes, It has lot more things like:

code quality, matrix, design, dependency, naming conversion, purity, visibility, architecture, layering and even the dead codes in your application.

Take a look into this result shot-

clip_image010

And Also thing like:

  • Warnings on Build Process Health
  • Harness Test Coverage Data
  • API and Power tools.

But the most exciting features that I like here is Code Rule and Code Query over LINQ. Its Amazing!!!!! Also it has the intelligence support.

clip_image012

Here are some examples that I have tried to my application

Here in first example I have tried to look into the type, found no of methods and field declared there-

clip_image013

And another example to search all fields which start with a certain string – say Jericho and also export the result into HTML –

Query Result

It seems pretty cool to me that allow me to deep drive into my application.

Hope it will help you to introduce with Ndepend and Code analyzing. Thanks guys for reading Smile.

Read Full Post | Make a Comment ( None so far )

Liked it here?
Why not try sites on the blogroll...

%d bloggers like this: