Entity Framework

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

Advertisements
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 )

Traversing an object Tree to the end in EF4

Posted on July 16, 2012. Filed under: C#, CodeProject, EF4, Entity Framework | Tags: , , |


Years ago, I read about Golden rule of Entity Framework from a blog post of Julia Lerman.As the Golden Rule says –

EF will not do anything that developer has not explicitly told you to do.

Some days ago I got an issue where I need to load a full object graph will all its relationship. It means I need to load the object with all children and also with grand children of the each child which means I need to traverse the full tree of the object through their relationship/navigation until I will reach to leaf.

For Example, The tree can be represent as:

<Customer>
    ...
    <Orders>
      <Order>
        ...
        <Order_Details>
          </Order_Detail>
          ... .. .. .. .. .. So on.. .. .. (continue)
          </Order_Detail>
          ...
        </Order_Details>
      </Order>
      ...
    </Orders>
  ...
  </Customer> 

Or can be represent as:

Customar

   |

   |

   -----Orders

          |

          |

          ------OrderDetails

|

|________.. .. .. .. So on .. ..(continue)

 

So I don’t know how much depth of the object-tree will be and that’s why I am going to avoid the LINQ way. You can also

do this by disabling the Lazy-loading but POCO entities do not have the same relationship requirements as objects that

inherit from EntityObject, a slightly different process is required to load related objects. Ok, it is a very simple matter

of Recursive solution by loading entity for the each navigation object. To load the navigation property I got

simple and useful method there-

 

ObjectContext.LoadProperty Method (Object, String)

public void LoadProperty(
    Object entity,
    string navigationProperty)
 

Before going into my recursive method, I would like to talk about my Entity. All of my entity is child of “BaseDataContract”

class. While traversing to the object you can set a list of data type also to ignore while traversing them. Here I check the

navigation property whether that is a one or many relationship with current entity. If it is one –then I simply call the

same method that it is currently in Recursive way; otherwise is call another Generic method which calls the Traverse method

for each entity in a collection for many relationship ends.

private ObjectContext _ctx;
private bool IsInheritFromType(Type childType, Type parentType)
        {
            var type = childType;
            while (type.BaseType != null)
            {
                if (type.BaseType.Equals(parentType))
                    return true;

                type = type.BaseType;
            }
            return false;
        }    
        /// <summary>
        /// Traverse to the end and load the related entity. 
        /// </summary>
        /// <param name="entity">Set the entity from which start traversing</param>
        /// <param name="parentType">Set the Parent to ignore the recurtion. For root entity, set it null</param>
        /// <param name="ignoreType">Some Type of entity I want to Ignore</param>
        /// <returns></returns>
        public void TraverseToTheEnd<T>(T entity, Type parentType =null ,Type[] ignoreType =null ) where T : BaseDataContract
        {
            if (null != entity)
            {
                var navigationProperties = _ctx.GetNavigationProperty<T>();
                if (null != navigationProperties)
                {
                    foreach (var navigationProperty in navigationProperties)
                    {
                        var info = entity.GetType().GetProperty(navigationProperty.Name);

                        if (null != info)
                        {
                            Type t = info.PropertyType;
                            if (null == ignoreType || !Array.Exists(ignoreType, i => i.Equals(t)))
                            {
                                if (((AssociationType) navigationProperty.RelationshipType).IsForeignKey)
                                {
                                    _ctx.LoadProperty(entity, navigationProperty.Name);

                                    var value = info.GetValue(entity, null);
                                    if (value != null)
                                    {
                                        Type baseType;
                                        if (!t.IsGenericType)
                                        {
                                            #region One to One or Zero

                                            baseType = t.BaseType;
                                            if (null != baseType && (parentType == null || !t.Equals(parentType))
                                                //avoid the recursion, dont back to parent 
                                                && baseType.BaseType != null
                                                && IsInheritFromType(baseType, typeof (BaseDataContract)))
                                            {
                                                //Traverse each child
                                                MethodInfo method = this.GetType().GetMethod("TraverseToTheEnd");
                                                MethodInfo generic = method.MakeGenericMethod(t);
                                                generic.Invoke(this, new object[] {value, typeof (T), ignoreType});
                                            }

                                            #endregion
                                        }
                                        else
                                        {
                                            #region One to Many

                                            Type[] genericBases = t.GetGenericArguments();
                                            // Q&D: pick the first one
                                            baseType = genericBases[0];
                                            if (null != baseType && (parentType == null || !baseType.Equals(parentType))
                                                //avoid the recursion, dont back to parent
                                                && baseType.BaseType != null &&
                                                IsInheritFromType(baseType, typeof (BaseDataContract)))
                                            {
                                                //Traverse each child
                                                MethodInfo method =
                                                    this.GetType().GetMethod("TraverseToTheEndForCollection");
                                                MethodInfo generic = method.MakeGenericMethod(baseType);
                                                generic.Invoke(this, new object[] {value, typeof (T), ignoreType});
                                            }

                                            #endregion
                                        }

                                    } //end if 
                                } //end if
                            }//end if                            
                        }
                    }//end foreach
                }//end if
            }//end if 
        }
        /// <summary>
        /// Traverse each entity of a collection to their end and load the related entity. 
        /// </summary>
        /// <param name="entities">Set the collection of entity for traversing each entity</param>
        /// <param name="parentType">Set the Parent to ignore the recurtion. For root entity, set it null</param>
        /// <param name="ignoreType">Some Type of entity I want to Ignore</param>
        /// <returns></returns>
        public void TraverseToTheEndForCollection<T>(FixupCollection<T> entities, Type parentType =null ,Type[] ignoreType =null )
           where T : BaseDataContract
        {
            if (null != entities)
            {
                for (int i = 0; i < entities.Count; i++)
                {
                    TraverseToTheEnd(entities[i], parentType, ignoreType);
                }
            }
        }

To load the navigation property value I just call the above method –loadProperty which is provided by the ObjectContext 

_ctx.LoadProperty(entity, navigationProperty.Name);

var value = info.GetValue(entity, null);

 

After that , Check the navigation property type whether that is generic type or not. in EF , Generic navigation type is

used for Collection of entity in case of Many End and on the other hand normal property type for One or Zero End.

Here, The method –IsInheritFromType is nothing but check for BaseType to some desired parent Type.This has been checked

to determine whether it is database object or not. To call a Generic method -

 

MethodInfo method = this.GetType().GetMethod("TraverseToTheEnd");

MethodInfo generic = method.MakeGenericMethod(t);

generic.Invoke(this, new object[] {value, typeof (T), ignoreType});

 

And TraverseToTheEndForCollection method has been called in the same way with the parameter of child/navigation property value

parent type and other paramters.

 

Here I need to create some extension of ObjectContext to get all navigation properties of an entity by manipulating

the metadata information. In GetNavigationProperty method we need to retrieve the entity set name (that’s why we need the

GetEntitySet method) and from its type members it check for nevigationProperty as BuiltInTypeKind.

 

public static EntitySetBase GetEntitySet(this ObjectContext context, Type entityType)
      {
          if (context == null)
          {
              throw new ArgumentNullException("context");
          }
          if (entityType == null)
          {
              throw new ArgumentNullException("entityType");
          }
          EntityContainer container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
          EntitySetBase entitySet = container.BaseEntitySets.Where(item => item.ElementType.Name.Equals(entityType.Name))
                                                            .FirstOrDefault();
          return entitySet;
      }

      public static List<NavigationProperty> GetNavigationProperty<T>(
      this ObjectContext context)
      {
          var containerName = context.DefaultContainerName;
          var model = DataSpace.CSpace;
          var workspace = context.MetadataWorkspace;
          var container = workspace.GetEntityContainer(containerName, model);
          EntitySetBase entitySet = context.GetEntitySet(typeof(T));

          if (entitySet == null)
              return null;

          //materialize nav props for testing
          var navigationProps = entitySet.ElementType.Members
              .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty
              )
              .Cast<NavigationProperty>()
              .ToList();

          return navigationProps;
      }

Ok that is it.

Yes you can ask me why I am going to such great length. Answer is-  because I want to backup the object tree into a backup file and later on it will be attached to database that might be the same database or another one with same schema. It will not override any entity if that any data of this object-tree already exist, if not then it will insert the object with related object as it is currently in its own. But that’s a different story. This method gives an object tree with all its related objects to the leaf and I can do whatever I want. For now I would like to serialize this object in a file and on the next post I will try to re-attach the object to database. Thanks for reading and Hope you like it Smile.

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

Implementing repository Pattern With EF4 POCO support

Posted on June 30, 2011. Filed under: C#, EF4, Entity Framework, Expression | Tags: , , , , , |


About two years ago have written a post and an article in about the repository pattern implementation with EF V1. After that I got a lot of requests in my articles to make it compatible for EF4 POCO support. So here I am going to make that. In this implementation of repository, I am going to add some new methods which has helped me to regularly while working with EF4 POCO support.

Hope it will help you- So here BaseContext is inherited from ObjectContext and BaseDataContract is base class of all my entity as POCO.

       public enum State
       {
           Unchanged, Added, Modified, Deleted
       }
       [DataContractAttribute(IsReference = true)]
       public abstract class BaseDataContract 
       {
           /// <summary>
           /// This string holds all the validation-error messages.
           /// </summary>
           /// 
           public BaseDataContract()
           {                                         
           }
   
           [DataMember]
           public State ObjectState { get; set; }       
       }
Here all the method responsible to do query, return result in ObjectQuery Which have been used for a special reason and 
that is ObjectQuery.EnablePlanCachingProperty that indicates whether the query plan should be cached. Plan-caching caches
information which is computed as part of putting together the query itself. By caching this, a subsequent execution of 
the same query (even if you change parameter values) will run much faster than the first one. 
 
This information is cached per app-domain so you will generally benefit from the query cache across multiple 
client requests to the same web app and the like. Here all DoQuery methods are responsible to queries and other
query method like SelectAll or Select methods internally use these DoQuery methods with various parameters.
SelectByKey is creating a LINQ expression using Expression tree for Primary key and During creating the repository
on an entity you can supply this Primary Key property as string. While using the POCO as you entity you can set 
attribute programming to serve this kind of job. 

TrySameValueExist is doing the same job by allow you to set customize field and value comparison there. What will do is create the expression for you and also add the PrimaryKey comparison so that it excludes the object that you are querying for (and where PrimaryKey != currentObjectPrimaryKey).

Add and Delete method simply call the AddObject and DeleteObject Method of ObjectContext. AddOrAttach method is for special situation where you don’t know whether object is already added or not. It is expensive since it will do query to database to check the existence.

ChangeObjectStatetoAttach is a method which update the state of an entity and also go  for all its navigation/related child object to change their state in recursive ways. To find out the navigation object I need to add extension method of ObjectContext which has been described later in Bulk delete feature.Here I check the navigation property type whether that is generic type or not. In EF, Generic navigation type is used for Collection of entity in case of Many End and on the other hand normal property type for One or Zero End..This has been checked to determine whether it is database object or not. To call a Generic method

 

 MethodInfo method = this.GetType().GetMethod("ChangeCollectionObjectStatetoAttach");

 MethodInfo generic = method.MakeGenericMethod(baseType);

 generic.Invoke(this, new object[] { value, state });

And ChangeCollectionObjectStatetoAttach method has been called in the same way with the parameter of parent, child/navigation property-value and state. Now it is time to Take a look Repository Class here :

public class Repository<E, C> : IRepository<E, C>, IDisposable
    where E : BaseDataContract 
    where C : BaseContext
{
    private C _ctx;

    private string _KeyProperty = "ID";

    public string KeyProperty
    {
        get
        {
            return _KeyProperty;
        }
        set
        {
            _KeyProperty = value;
        }
    }

    public C Session
    {
        get { return _ctx; }
        set { _ctx = value; }
    }

    public Repository(C session)
    {
        _ctx = session;
    }

    public Repository(C session, string keyProperty) : this(session)
    {
        _KeyProperty = keyProperty;
    }

    #region IRepository<E,C> Members

    public int Save()
    {
        return _ctx.SaveChanges();
    }
    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// the parameter entitySetName in the resolution of mapping
    /// between the CSDL and SSDL. In Version
    /// 2.0 this pluralizing behaviour will change and this
    /// method overload should be used only if Developers
    /// change EntitySet names from the default name generated.
    /// </summary>
    /// <param name=”entitySetName”>
    /// The EntitySet name of the entity in the model.
    /// </param>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> DoQuery(string entitySetName)
    {
        _ctx.ContextOptions.LazyLoadingEnabled = true;
        return _ctx.CreateQuery<E>("[" + entitySetName + "]");
    }
    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// typeof(TEntity).Name in the resolution of mapping
    /// between the CSDL and SSDL. This method is useful
    /// for Models that DO NOT have their pluralized names
    /// changed by the developer.
    /// </summary>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> DoQuery()
    {
        _ctx.ContextOptions.LazyLoadingEnabled = true;
        return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]");
    }

    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// the parameter entitySetName in the resolution of mapping
    /// between the CSDL and SSDL. This method is useful
    /// for Models that DO have their pluralized names
    /// changed by the developer.
    /// </summary>
    /// <param name=”entitySetName”>
    /// The EntitySet name of the entity in the model.
    /// </param>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
    {
        _ctx.ContextOptions.LazyLoadingEnabled = true;
        return
            (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
            .Where(where.EvalPredicate);
    }

    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// typeof(TEntity).Name in the resolution of mapping
    /// between the CSDL and SSDL. This method is useful
    /// for Models that DO NOT have their pluralized names
    /// changed by the developer. 
    /// </summary>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> DoQuery(ISpecification<E> where)
    {
        _ctx.ContextOptions.LazyLoadingEnabled = true;
        return
            (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + typeof(E).Name + "]")
            .Where(where.EvalPredicate);
    }
    /// <summary>
    /// Query Entity with Paging 
    /// </summary>
    /// <param name="maximumRows">Max no of row to Fetch</param>
    /// <param name="startRowIndex">Start Index</param>
    /// <returns>Collection of Entities</returns>
    public ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex)
    {
        _ctx.ContextOptions.LazyLoadingEnabled = true;
        return (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Skip<E>(startRowIndex).Take(maximumRows);
    }
    /// <summary>
    /// Query Entity in sorted Order
    /// </summary>
    /// <param name="sortExpression">Sort Expression/condition</param>
    /// <param name="ErrorCode">custom Error Message</param> 
    /// <returns>Collection of Entities</returns>
    public ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression)
    {
        if (null == sortExpression)
        {
            return ((IRepository<E, C>)this).DoQuery();
        }
        return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery().OrderBy<E, object>(sortExpression);
    }
    /// <summary>
    /// Query All Entity in sorted Order with Paging support
    /// </summary>
    /// <param name="sortExpression">Sort Expression/condition</param>
    /// <param name="maximumRows">Max no of row to Fetch</param>
    /// <param name="startRowIndex">Start Index</param>
    /// <returns>Collection Of entites</returns>
    public ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression, int maximumRows, int startRowIndex)
    {
        if (sortExpression == null)
        {
            return ((IRepository<E, C>)this).DoQuery(maximumRows, startRowIndex);
        }
        return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery(sortExpression).Skip<E>(startRowIndex).Take(maximumRows);
    }
    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// the parameter entitySetName in the resolution of mapping
    /// between the CSDL and SSDL. In Version
    /// 2.0 this pluralizing behaviour will change and this
    /// method overload should be used only if Developers
    /// change EntitySet names from the default name generated.
    /// </summary>
    /// <param name=”entitySetName”>
    /// The EntitySet name of the entity in the model.
    /// </param>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> SelectAll(string entitySetName)
    {
        return DoQuery(entitySetName);
    }
    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// typeof(TEntity).Name in the resolution of mapping
    /// between the CSDL and SSDL. This method is useful
    /// for Models that DO NOT have their pluralized names
    /// changed by the developer.
    /// </summary>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> SelectAll()
    {
        try
        {
            return DoQuery(); //_ctx.CreateQuery<E>("[" + typeof(E).Name + "]");
        }
        catch (Exception)
        {
            throw;
        }
    }

    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// the parameter entitySetName in the resolution of mapping
    /// between the CSDL and SSDL. This method is useful
    /// for Models that DO have their pluralized names
    /// changed by the developer.
    /// </summary>
    /// <param name=”entitySetName”>
    /// The EntitySet name of the entity in the model.
    /// </param>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> SelectAll(string entitySetName, ISpecification<E> where)
    {
        return DoQuery(entitySetName, where);
    }

    /// <summary>
    /// A generic method to return ALL the entities
    /// of type TEntity. This overload will use the
    /// typeof(TEntity).Name in the resolution of mapping
    /// between the CSDL and SSDL. This method is useful
    /// for Models that DO NOT have their pluralized names
    /// changed by the developer. 
    /// </summary>
    /// <typeparam name=”TEntity”>
    /// The Entity to load from the database.
    /// </typeparam>
    /// <returns>Returns a set of TEntity.</returns>
    public ObjectQuery<E> SelectAll(ISpecification<E> where)
    {
        return DoQuery(where);
    }
    /// <summary>
    /// Select All Entity with Paging 
    /// </summary>
    /// <param name="maximumRows">Max no of row to Fetch</param>
    /// <param name="startRowIndex">Start Index</param>
    /// <returns>Collection of Entities</returns>
    public ObjectQuery<E> SelectAll(int maximumRows, int startRowIndex)
    {
        return DoQuery(maximumRows, startRowIndex);
    }
    /// <summary>
    /// Select All Entity in sorted Order
    /// </summary>
    /// <param name="sortExpression">Sort Expression/condition</param>
    /// <param name="ErrorCode">custom Error Message</param> 
    /// <returns>Collection of Entities</returns>
    public ObjectQuery<E> SelectAll(Expression<Func<E, object>> sortExpression)
    {
        if (null == sortExpression)
        {
            return DoQuery(sortExpression);
        }
        return DoQuery(sortExpression);
    }
    /// <summary>
    /// Select All Entity in sorted Order with Paging support
    /// </summary>
    /// <param name="sortExpression">Sort Expression/condition</param>
    /// <param name="maximumRows">Max no of row to Fetch</param>
    /// <param name="startRowIndex">Start Index</param>
    /// <returns>Collection Of entites</returns>
    public ObjectQuery<E> SelectAll(Expression<Func<E, object>> sortExpression, int maximumRows, int startRowIndex)
    {
        if (sortExpression == null)
        {
            return DoQuery(maximumRows, startRowIndex);
        }
        return DoQuery(sortExpression, maximumRows, startRowIndex);
    }
    /// <summary>
    /// Get Entity By Primary Key
    /// </summary>
    /// <typeparam name="E">Entity Type</typeparam>
    /// <param name="Key">Primary Key Value</param>
    /// <returns>return entity</returns>
    public E SelectByKey(string Key)
    {
        _ctx.ContextOptions.LazyLoadingEnabled = true;
        // First we define the parameter that we are going to use the clause. 
        var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
        MemberExpression leftExpr = MemberExpression.Property(xParam, this._KeyProperty);
        Expression rightExpr = Expression.Constant(Key);
        BinaryExpression binaryExpr = MemberExpression.Equal(leftExpr, rightExpr);
        //Create Lambda Expression for the selection 
        Expression<Func<E, bool>> lambdaExpr = Expression.Lambda<Func<E, bool>>(binaryExpr, new ParameterExpression[] { xParam });
        //Searching ....
        ObjectQuery<E> resultCollection = ((IRepository<E, C>)this).SelectAll(new Specification<E>(lambdaExpr));
        if (null != resultCollection && resultCollection.Count() > 0)
        {
            //return valid single result
            return resultCollection.First<E>();
        }//end if 
        return null;
    }
    /// <summary>
    /// Check if value of specific field is already exist
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <param name="fieldName">name of the Field</param>
    /// <param name="fieldValue">Field value</param>
    /// <param name="key">Primary key value</param>
    /// <returns>True or False</returns>
    public bool TrySameValueExist(string fieldName, object fieldValue, string key)
    {
        // First we define the parameter that we are going to use the clause. 
        var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
        MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, fieldName);
        Expression rightExprFieldCheck = Expression.Constant(fieldValue);
        BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);

        MemberExpression leftExprKeyCheck = MemberExpression.Property(xParam, this._KeyProperty);
        Expression rightExprKeyCheck = Expression.Constant(key);
        BinaryExpression binaryExprKeyCheck = MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
        BinaryExpression finalBinaryExpr = Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);

        //Create Lambda Expression for the selection 
        Expression<Func<E, bool>> lambdaExpr = Expression.Lambda<Func<E, bool>>(finalBinaryExpr, new ParameterExpression[] { xParam });
        //Searching ....            
        return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
    }
    /// <summary>
    /// Check if Entities exist with Condition
    /// </summary>
    /// <param name="selectExpression">Selection Condition</param>
    /// <returns>True or False</returns>
    public bool TryEntity(ISpecification<E> selectSpec)
    {
        return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>(selectSpec.EvalPredicate);
    }
    /// <summary>
    /// Get Count of all records
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <returns>count of all records</returns>
    public int GetCount()
    {
        return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Count();
    }
    /// <summary>
    /// Get count of selection
    /// </summary>
    /// <typeparam name="E">Selection Condition</typeparam>
    /// <returns>count of selection</returns>
    public int GetCount(ISpecification<E> selectSpec)
    {
        return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]")
            .Where(selectSpec.EvalPredicate).Count();
    }
    /// <summary>
    /// Delete data from context
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <param name="entity"></param>
    public void Delete(E entity)
    {
        //object deletedObject;
        //_ctx.TryGetObjectByKey(entity.EntityKey, out deletedObject);
        _ctx.DeleteObject(entity);
    }
    /// <summary>
    /// Delete data from context
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <param name="entity"></param>
    public void Delete(object entity)
    {
        //object deletedObject;
        //_ctx.TryGetObjectByKey(((EntityObject)entity).EntityKey, out deletedObject);
        _ctx.DeleteObject(entity);
    }
    /// <summary>
    /// Insert new data into context
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <param name="entity"></param>
    public void Add(E entity)
    {
        _ctx.AddObject(entity.GetType().Name, entity);
    }
    /// <summary>
    /// Insert if new otherwise attach data into context. Plus, 
    /// returns a list of changed property name and value (in 
    /// this format--> "propName|originalValue|newValue")- if any.
    /// </summary>
    /// <param name="entity"></param>
    public List<String> AddOrAttach(E entity, EntityKey key = null)
    {
        try
        {
            _ctx.ContextOptions.LazyLoadingEnabled = false;

            ObjectStateEntry entry = null;
            //Add the object graph into the context
            if (null == key )
            {
                var entitySet = _ctx.GetEntitySet(entity.GetType());
                key = _ctx.CreateEntityKey(entitySet.Name, entity);
            }
        
            if( null != key )
                _ctx.ObjectStateManager.TryGetObjectStateEntry(key, out entry);
            if (entry == null)
            {
                _ctx.AddObject(typeof(E).Name, entity);
                _ctx.ObjectStateManager.ChangeObjectState(entity, StateHelpers.GetEquivelantEntityState(entity.ObjectState));                
            }
            else
            {
                _ctx.ApplyCurrentValues(typeof(E).Name, entity);
            }           
            //get the changed column values (needed for audit trail)
            List<String> changedCols = new List<string>();
            var ose = (entry != null)? entry : _ctx.ObjectStateManager.GetObjectStateEntry(entity);
            foreach (var propName in ose.GetModifiedProperties())
            {
                string pNameValue = propName;
                pNameValue += "|" + ose.OriginalValues[propName] + "|" + ose.CurrentValues[propName];
                changedCols.Add(pNameValue);
            }

            return changedCols;
        }
        catch (Exception)
        {                    
            throw;
        }
    }
    /// <summary>
    /// Start Transaction
    /// </summary>
    /// <returns></returns>
    public DbTransaction BeginTransaction()
    {
        if (_ctx.Connection.State != ConnectionState.Open)
        {
            _ctx.Connection.Open();
        }
        return _ctx.Connection.BeginTransaction();
    }
    /// <summary>
    /// Change state of Navigation Object(s)
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <typeparam name="T"></typeparam>
    /// <param name="context"></param>
    /// <param name="entity"></param>
    /// <param name="state"></param>
    public void ChangeStateOfNavigationObject(E entity, State state)
    {
        if (null != entity)
        {
            var navigationProperties = _ctx.GetNavigationProperty<C, E>();
            if (null != navigationProperties)
            {
                foreach (var navigationProperty in navigationProperties)
                {
                    var info = entity.GetType().GetProperty(navigationProperty.Name);
                    var value = info.GetValue(entity, null);

                    if (value != null)
                    {

                        if (((AssociationType)navigationProperty.RelationshipType).IsForeignKey
                            &&
                            navigationProperty.FromEndMember.RelationshipMultiplicity !=
                            RelationshipMultiplicity.Many)
                        {
                            Type t = value.GetType();
                            Type baseType;
                            if (!t.IsGenericType)
                            {
                                baseType = t.BaseType;

                                if (baseType.BaseType.Equals(typeof(BaseDataContract)))
                                {
                                    MethodInfo method = this.GetType().GetMethod("ChangeObjectStatetoAttach");
                                    MethodInfo generic = method.MakeGenericMethod(baseType);
                                    generic.Invoke(this, new object[] { value, state });
                                }
                            }
                            else
                            {
                                Type[] genericBases = t.GetGenericArguments();
                                // Q&D: pick the first one
                                baseType = genericBases[0];

                                if (baseType.BaseType.Equals(typeof(BaseDataContract)))
                                {
                                    MethodInfo method =
                                        this.GetType().GetMethod("ChangeCollectionObjectStatetoAttach");
                                    MethodInfo generic = method.MakeGenericMethod(baseType);
                                    generic.Invoke(this, new object[] { value, state });
                                }
                            }
                        }
                        else
                        {
                            info.SetValue(entity, null, null);
                        }
                    }//end if
                }
            }
        }
    }
    /// <summary>
    /// change state of an single object
    /// </summary>
    /// <param name="entity"></param>
    /// <param name="state"></param>
    public void ChangeObjectStatetoAttach<T>(T entity, State state) where T : BaseDataContract 
    {
        if (null != entity)
        {
            var dataContract = entity as BaseDataContract;
            dataContract.ObjectState = state;
            EntityKey entityKey = _ctx.CreateEntityKey(
                _ctx.GetEntitySet(dataContract.GetType()).Name, dataContract);
            ObjectStateEntry storeEntity;
            if (_ctx.ObjectStateManager.TryGetObjectStateEntry(entityKey, out storeEntity))
            {
                _ctx.ApplyCurrentValues(_ctx.GetEntitySet(typeof(T)).Name, entity);
                //(storeEntity as T).ObjectState = State.Unchanged;
            }
            else
            {
                _ctx.AddObject(_ctx.GetEntitySet(dataContract.GetType()).Name, dataContract);
            }
            _ctx.ObjectStateManager.ChangeObjectState(dataContract,
                                                         StateHelpers.
                                                             GetEquivelantEntityState
                                                             (dataContract.ObjectState));
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="entities"></param>
    /// <param name="state"></param>
    public void ChangeCollectionObjectStatetoAttach<T>(FixupCollection<T> entities, State state)
       where T : BaseDataContract
    {
        if (null != entities)
        {
            for (int i = 0; i < entities.Count; i++)
            {
                ChangeObjectStatetoAttach<T>(entities[i], state);
            }
        }
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        if (null != _ctx)
        {
            if (_ctx.Connection.State == ConnectionState.Open)
                _ctx.Connection.Close();
            _ctx.Dispose();
        }
    }

    #endregion

}

 

Here is StateHelpers Class which have been used for Retrieving the Object State that compatible with Entity Framework.
 
public static class StateHelpers
    {
        public static EntityState GetEquivelantEntityState(State state)
        {
            //this handy method comes from Rowan Miller on the EF team!
            switch (state)
            {
                case State.Added:
                    return EntityState.Added;
                case State.Modified:
                    return EntityState.Modified;
                case State.Deleted:
                    return EntityState.Deleted;
                default:
                    return EntityState.Unchanged;
            }
        }
    }
 
 
 
To work with some bulk operation and getting related entities on runtime,  I have added some extension methods to find out related navigation info – 
   1: public static List<NavigationProperty> GetNavigationProperty<TObjectContext, T>(

   2:         this ObjectContext context)

   3:         where TObjectContext : ObjectContext

   4:          {

   5:              var containerName = context.DefaultContainerName;

   6:              var model = DataSpace.CSpace;

   7:              var workspace = context.MetadataWorkspace;

   8:              var container = workspace.GetEntityContainer(containerName, model);

   9:              EntitySetBase entitySet = context.GetEntitySet(typeof (T));

  10:  

  11:              if (entitySet == null)

  12:                  return null;

  13:  

  14:              //materialize nav props for testing

  15:              var navigationProps = entitySet.ElementType.Members

  16:                  .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty

  17:                  )

  18:                  .Cast<NavigationProperty>()

  19:                  .ToList();

  20:  

  21:             return navigationProps;

  22:          }

  23:   

  24:         public static EntitySetBase GetEntitySet(this ObjectContext context, Type entityType)

  25:         {

  26:             if (context == null)

  27:             {

  28:                 throw new ArgumentNullException("context");

  29:             }

  30:             if (entityType == null)

  31:             {

  32:                 throw new ArgumentNullException("entityType");

  33:             }

  34:             EntityContainer container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);

  35:             if (container == null)

  36:             {

  37:                 return null;

  38:             }

  39:             EntitySetBase entitySet = container.BaseEntitySets.Where(item => item.ElementType.Name.Equals(entityType.Name))

  40:                                                               .FirstOrDefault();

  41:             return entitySet;

  42:         }

 

Also some methods in context which I got from my very favorite Matthieu MEZIL post of bulk delete operation

   1: public class BaseContext : ObjectContext, IObjectContextWithBulkOperations

   2:     {

   3:         #region Constructors

   4:  

   5:         public BaseContext(string connectionString, string containerName)

   6:             : base(connectionString, containerName)

   7:         {

   8:             this.ContextOptions.LazyLoadingEnabled = false;

   9:         }

  10:  

  11:         public BaseContext(EntityConnection connection, string containerName)

  12:             : base(connection, containerName)

  13:         {

  14:             this.ContextOptions.LazyLoadingEnabled = false;

  15:         }

  16:     

  17:         #endregion

  18:  

  19:         #region IContext Members

  20:  

  21:         public string Save()

  22:         {

  23:             string validationErrors;

  24:             if (ValidateBeforeSave(out validationErrors))

  25:             {

  26:                 //StateFixup();

  27:                 SaveChanges();

  28:                 return "";

  29:             }

  30:             return "Data Not Saved due to Validation Errors: " + validationErrors;

  31:         }

  32:  

  33:         public IEnumerable<T> ManagedEntities<T>()

  34:         {

  35:             var oses =

  36:                 ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified |

  37:                                                          EntityState.Unchanged | EntityState.Detached);

  38:             return oses.Where(entry => entry.Entity is T)

  39:                        .Select(entry => (T)entry.Entity);

  40:         }

  41:  

  42:         public bool ValidateBeforeSave(out string validationErrors)

  43:         {

  44:             bool isvalid = true;

  45:             validationErrors = "";

  46:             return isvalid;

  47:         }

  48:  

  49:         public void ChangeState<T>(State state, T entity) where T : class

  50:         {

  51:             ObjectStateManager.ChangeObjectState(entity, StateHelpers.GetEquivelantEntityState(state));

  52:         }

  53:  

  54:         #endregion

  55:  

  56:         public void UpdateTrackedEntity<T>(T modifiedEntity) where T : class

  57:         {

  58:             var set = this.CreateObjectSet<T>();

  59:             set.ApplyCurrentValues(modifiedEntity);

  60:         }

  61:  

  62:         #region IObjectContextWithBulkOperations Member

  63:  

  64:         private List<Action> _bulkDeletedActions;

  65:         private List<Action> BulkDeletedActions

  66:         {

  67:             get

  68:             {

  69:                 if (_bulkDeletedActions == null)

  70:                     _bulkDeletedActions = new List<Action>();

  71:                 return _bulkDeletedActions;

  72:             }

  73:         }

  74:  

  75:         private List<object> _bulkDeletedEntities;

  76:         public List<object> BulkDeletedEntities

  77:         {

  78:             get

  79:             {

  80:                 if (_bulkDeletedEntities == null)

  81:                     _bulkDeletedEntities = new List<object>();

  82:                 return _bulkDeletedEntities;

  83:             }

  84:         }

  85:  

  86:         private Dictionary<Type, List<Func<object, bool>>> _bulkDeletedFuncs;

  87:         public Dictionary<Type, List<Func<object, bool>>> BulkDeletedFuncs

  88:         {

  89:             get

  90:             {

  91:                 if (_bulkDeletedFuncs == null)

  92:                     _bulkDeletedFuncs = new Dictionary<Type, List<Func<object, bool>>>();

  93:                 return _bulkDeletedFuncs;

  94:             }

  95:         }

  96:  

  97:         public void Delete<TBase, T>(ObjectSet<TBase> entitySet, Expression<Func<T, bool>> predicate)

  98:             where T : class, TBase

  99:             where TBase : class

 100:         {

 101:             Delete<TBase, T>(entitySet, predicate, true);

 102:         }

 103:         public void DeleteLoadedEntities<TBase, T>(ObjectSet<TBase> entitySet, Expression<Func<T, bool>> predicate)

 104:             where T : class, TBase

 105:             where TBase : class

 106:         {

 107:             if ((predicate = CalculatePredicate(entitySet, predicate)) != null)

 108:                 Delete<TBase, T>(entitySet, predicate, false);

 109:         }

 110:  

 111:         private Expression<Func<T, bool>> CalculatePredicate<TBase, T>(ObjectSet<TBase> entitySet, Expression<Func<T, bool>> oldPredicate)

 112:             where T : class, TBase

 113:             where TBase : class

 114:         {

 115:             IEnumerable<PropertyInfo> keyMembers = entitySet.EntitySet.ElementType.KeyMembers.Select(km => typeof(T).GetProperty(km.Name)).ToList();

 116:             IEnumerable<T> entitiesEnumerable = ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged)

 117:                                                 .Select(ose => ose.Entity)

 118:                                                 .OfType<T>();

 119:             ParameterExpression parameter = oldPredicate.Parameters.Single();

 120:             if (!entitiesEnumerable.Any())

 121:                 return null;

 122:             return Expression.Lambda<Func<T, bool>>(

 123:                 Expression.AndAlso(

 124:                     oldPredicate.Body,

 125:                     entitiesEnumerable.Select(e =>

 126:                         keyMembers.Select(km =>

 127:                             Expression.Equal(

 128:                                 Expression.MakeMemberAccess(parameter, km),

 129:                                 Expression.Constant(km.GetValue(e, null))))

 130:                         .Aggregate((accumulate, clause) =>

 131:                             Expression.AndAlso(accumulate, clause)))

 132:                     .Aggregate((accumulate, clause) =>

 133:                         Expression.OrElse(accumulate, clause)))

 134:                 , oldPredicate.Parameters);

 135:         }

 136:  

 137:         private void Delete<TBase, T>(ObjectSet<TBase> entitySet, Expression<Func<T, bool>> predicate, bool propagateToFutureEntities)

 138:             where TBase : class

 139:             where T : class, TBase

 140:         {

 141:             ObjectQuery<T> objectQuery = (ObjectQuery<T>)entitySet.OfType<T>().Where(predicate);

 142:             string selectSQLQuery = objectQuery.ToTraceString();

 143:             List<KeyValuePair<string, List<string>>> froms = new List<KeyValuePair<string, List<string>>>();

 144:             Match fromMatch = Regex.Match(entitySet.OfType<T>().ToTraceString(), "(FROM|JOIN)[ ]+((\\[[^\\]]+\\]).)*\\[([^\\]]+)\\]");

 145:             List<AssociationType> ssdlAsscociations = MetadataWorkspace.GetItems(DataSpace.SSpace).OfType<AssociationType>().ToList();

 146:             string firstFrom = null;

 147:             while (fromMatch.Success)

 148:             {

 149:                 string fromValue = fromMatch.Groups[4].Value;

 150:                 if (Regex.IsMatch(selectSQLQuery, string.Format("(FROM|JOIN)[ ]+((\\[[^\\]]+\\]).)*\\[{0}\\]", fromValue)))

 151:                 {

 152:                     var index = (from ssdlAssociation in ssdlAsscociations

 153:                                  where ssdlAssociation.ReferentialConstraints.Any(rc => fromValue == rc.ToProperties.First().DeclaringType.Name)

 154:                                  from table in froms.Select((f, i) => new { Table = f, Index = i })

 155:                                  where ssdlAssociation.ReferentialConstraints.Any(rc => table.Table.Key == rc.FromProperties.First().DeclaringType.Name)

 156:                                  orderby table.Index

 157:                                  select new { Index = table.Index, SSDLAssociation = ssdlAssociation, FKs = table.Table }).FirstOrDefault();

 158:                     if (index != null)

 159:                         froms.Insert(index.Index, new KeyValuePair<string, List<string>>(fromValue, (from fk in index.FKs.Value

 160:                                                                                                      let referentailConstraint = index.SSDLAssociation.ReferentialConstraints.First(rc => index.FKs.Key == rc.FromProperties.First().DeclaringType.Name)

 161:                                                                                                      select referentailConstraint.ToProperties.ElementAt(referentailConstraint.FromProperties.Select((p, pIndex) => new { p.Name, Index = pIndex }).First(p => p.Name == fk).Index).Name).ToList()));

 162:                     else

 163:                     {

 164:                         if (firstFrom == null)

 165:                             firstFrom = fromValue;

 166:                         froms.Add(new KeyValuePair<string, List<string>>(fromValue, MetadataWorkspace.GetItems(DataSpace.SSpace).OfType<EntityType>().First(et => et.Name == fromValue).KeyMembers.Select(km => km.Name).ToList()));

 167:                     }

 168:                 }

 169:                 fromMatch = fromMatch.NextMatch();

 170:             }

 171:             StringBuilder delete = new StringBuilder();

 172:  

 173:             string selectSQLQueryWithoutSelect = selectSQLQuery.Substring(selectSQLQuery.IndexOf("FROM"));

 174:             IEnumerator<EdmMember> keyMembersEnumerator = null;

 175:  

 176:             if (froms.Count > 1)

 177:             {

 178:                 delete.Append("declare @DeleteIds table (");

 179:                 StringBuilder keys = new StringBuilder();

 180:                 keyMembersEnumerator = MetadataWorkspace.GetItems(DataSpace.SSpace).OfType<EntityType>().

 181:                     First(et => et.Name == firstFrom).KeyMembers.ToList().GetEnumerator();

 182:                 keyMembersEnumerator.MoveNext();

 183:                 for (; ; )

 184:                 {

 185:                     string keyName = keyMembersEnumerator.Current.Name;

 186:                     keys.Append(keyName);

 187:                     delete.Append(keyName);

 188:                     delete.Append(" ");

 189:                     delete.Append(keyMembersEnumerator.Current.TypeUsage.EdmType.Name);

 190:                     Facet maxLength = keyMembersEnumerator.Current.TypeUsage.Facets.FirstOrDefault(f => f.Name == "MaxLength");

 191:                     if (maxLength != null)

 192:                     {

 193:                         delete.Append("(");

 194:                         delete.Append(maxLength.Value);

 195:                         delete.Append(")");

 196:                     }

 197:                     if (keyMembersEnumerator.MoveNext())

 198:                     {

 199:                         keys.Append(", ");

 200:                         delete.Append(", ");

 201:                     }

 202:                     else

 203:                         break;

 204:                 }

 205:                 delete.Append(");\n");

 206:  

 207:                 delete.Append("INSERT INTO @DeleteIds SELECT ");

 208:                 delete.Append(keys.ToString());

 209:                 delete.Append(" ");

 210:                 delete.Append(selectSQLQueryWithoutSelect.Replace("@p__linq__", "@p"));

 211:                 delete.Append(";\n");

 212:             }

 213:  

 214:             foreach (KeyValuePair<string, List<string>> from in froms)

 215:             {

 216:                 delete.Append("DELETE FROM [");

 217:                 delete.Append(from.Key);

 218:                 delete.Append("] FROM ");

 219:  

 220:                 if (froms.Count > 1)

 221:                 {

 222:                     delete.Append("[");

 223:                     delete.Append(from.Key);

 224:                     delete.Append("]");

 225:                     delete.Append("INNER JOIN @deleteIds D ON ");

 226:  

 227:                     keyMembersEnumerator.Reset();

 228:                     keyMembersEnumerator.MoveNext();

 229:                     int index = 0;

 230:                     for (; ; )

 231:                     {

 232:                         delete.Append("[");

 233:                         delete.Append(from.Key);

 234:                         delete.Append("].");

 235:                         delete.Append(from.Value[index++]);

 236:                         delete.Append(" = D.");

 237:                         delete.Append(keyMembersEnumerator.Current);

 238:  

 239:                         if (keyMembersEnumerator.MoveNext())

 240:                             delete.Append(" AND ");

 241:                         else

 242:                             break;

 243:                     }

 244:                 }

 245:                 else

 246:                     delete.Append(selectSQLQueryWithoutSelect.Substring(4).TrimStart());

 247:  

 248:                 delete.Append(";\n");

 249:             }

 250:  

 251:             BulkDeletedActions.Add(() => ExecuteStoreCommand(delete.ToString(), objectQuery.Parameters.Select(p => p.Value).ToArray()));

 252:  

 253:             Func<T, bool> predicateCompiled = predicate.Compile();

 254:             Func<object, bool> predicateCompiledObject = o =>

 255:             {

 256:                 T t = o as T;

 257:                 if (t == null)

 258:                     return false;

 259:                 return predicateCompiled(t);

 260:             };

 261:             if (propagateToFutureEntities)

 262:             {

 263:                 List<Func<object, bool>> bulkDeletedFuncs;

 264:                 if (BulkDeletedFuncs.TryGetValue(typeof(TBase), out bulkDeletedFuncs))

 265:                     bulkDeletedFuncs.Add(predicateCompiledObject);

 266:                 else

 267:                     BulkDeletedFuncs.Add(typeof(TBase), new List<Func<object, bool>>() { predicateCompiledObject });

 268:             }

 269:             EntityType entityType = MetadataWorkspace.GetItems(DataSpace.CSpace).OfType<EntityType>().First(et => et.Name == typeof(T).Name);

 270:             var oneToOneSubEntityTypes = (from np in entityType.NavigationProperties

 271:                                           where np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One

 272:                                           let otherEntityType = np.ToEndMember.GetEntityType()

 273:                                           let otherNavigationProperty = otherEntityType.NavigationProperties.FirstOrDefault(otherNP => otherNP.RelationshipType == np.RelationshipType)

 274:                                           select new

 275:                                           {

 276:                                               EntityType = otherEntityType,

 277:                                               ClrType = typeof(T).GetProperty(np.Name).PropertyType,

 278:                                               OtherNavigationPropertyName = otherNavigationProperty == null ? null : otherNavigationProperty.Name,

 279:                                               ReferencialConstraint = ((AssociationType)np.RelationshipType).ReferentialConstraints.FirstOrDefault()

 280:                                           }).ToList();

 281:             foreach (var subEntityTypeLoop in oneToOneSubEntityTypes)

 282:             {

 283:                 var subEntityType = subEntityTypeLoop;

 284:                 if (subEntityType.OtherNavigationPropertyName != null)

 285:                 {

 286:                     List<string> entityTypeKeys, subEntityTypeKeys;

 287:                     if (subEntityType.ReferencialConstraint.FromProperties.First().DeclaringType == entityType)

 288:                     {

 289:                         entityTypeKeys = subEntityType.ReferencialConstraint.FromProperties.Select(p => p.Name).ToList();

 290:                         subEntityTypeKeys = subEntityType.ReferencialConstraint.ToProperties.Select(p => p.Name).ToList();

 291:                     }

 292:                     else

 293:                     {

 294:                         entityTypeKeys = subEntityType.ReferencialConstraint.ToProperties.Select(p => p.Name).ToList();

 295:                         subEntityTypeKeys = subEntityType.ReferencialConstraint.FromProperties.Select(p => p.Name).ToList();

 296:                     }

 297:                     ParameterExpression entityParameter = Expression.Parameter(typeof(object), "entity");

 298:                     ParameterExpression subEntityParameter = Expression.Parameter(typeof(object), "subEntity");

 299:                     Func<object, object, bool> associateToBulkEntities =

 300:                         Expression.Lambda<Func<object, object, bool>>(

 301:                             entityTypeKeys.Select((entityTypeKey, keyIndex) =>

 302:                                 Expression.Equal(

 303:                                     Expression.MakeMemberAccess(

 304:                                         Expression.Convert(

 305:                                             subEntityParameter,

 306:                                             subEntityType.ClrType),

 307:                                         subEntityType.ClrType.GetProperty(subEntityTypeKeys[keyIndex])),

 308:                                     Expression.MakeMemberAccess(

 309:                                         Expression.Convert(

 310:                                             entityParameter,

 311:                                             typeof(T)),

 312:                                         typeof(T).GetProperty(entityTypeKey)))).

 313:                             Aggregate((accumulate, keyPredicate) => Expression.AndAlso(accumulate, keyPredicate)),

 314:                             entityParameter,

 315:                             subEntityParameter).

 316:                             Compile();

 317:                     Func<object, bool> npPredicate = subE => BulkDeletedEntities.OfType<T>().Any(e => associateToBulkEntities(e, subE));

 318:  

 319:                     List<Func<object, bool>> bulkDeletedFuncs;

 320:                     if (BulkDeletedFuncs.TryGetValue(subEntityType.ClrType, out bulkDeletedFuncs))

 321:                         bulkDeletedFuncs.Add(npPredicate);

 322:                     else

 323:                         BulkDeletedFuncs.Add(subEntityType.ClrType, new List<Func<object, bool>>() { npPredicate });

 324:                 }

 325:             }

 326:             foreach (var entity in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged).

 327:                                     Select(ose => new { Entity = ose.Entity as T, ose.State }).

 328:                                     Where(e => e.Entity != null && predicateCompiled(e.Entity)))

 329:             {

 330:                 if (entity.State != EntityState.Deleted)

 331:                     DeleteObjectAndAddThemIntoBulkDeletedEntities(entity.Entity);

 332:                 else

 333:                 {

 334:                     BulkDeletedEntities.Add(entity.Entity);

 335:                     foreach (var subEntity in oneToOneSubEntityTypes.

 336:                                                 SelectMany(subEntityType =>

 337:                                                     ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged).

 338:                                                     Where(ose => subEntityType.ClrType.IsAssignableFrom(ose.Entity.GetType()) && !BulkDeletedEntities.Contains(ose.Entity))))

 339:                         ApplyBulkDeletedFuncs(subEntity.Entity, subEntity.State);

 340:                 }

 341:             }

 342:         }

 343:  

 344:         private void ApplyBulkDeletedFuncs(object entity, EntityState entityState)

 345:         {

 346:             List<Func<object, bool>> bulkDeletedFuncs;

 347:             if (_bulkDeletedFuncs != null)

 348:             {

 349:                 Type t = entity.GetType();

 350:                 do

 351:                 {

 352:                     if (BulkDeletedFuncs.TryGetValue(t, out bulkDeletedFuncs))

 353:                         foreach (Func<object, bool> bulkDeletedFunc in bulkDeletedFuncs)

 354:                             if (bulkDeletedFunc(entity))

 355:                             {

 356:                                 if (entityState != EntityState.Deleted)

 357:                                     DeleteObjectAndAddThemIntoBulkDeletedEntities(entity);

 358:                                 else

 359:                                     BulkDeletedEntities.Add(entity);

 360:                                 return;

 361:                             }

 362:                 } while ((t = t.BaseType) != null);

 363:             }

 364:         }

 365:  

 366:         private void DeleteObjectAndAddThemIntoBulkDeletedEntities(object entity)

 367:         {

 368:             CollectionChangeEventHandler objectStateManagerObjectStateManagerChanged = (sender, e) => BulkDeletedEntities.Add(e.Element);

 369:             ObjectStateManager.ObjectStateManagerChanged += objectStateManagerObjectStateManagerChanged;

 370:             DeleteObject(entity);

 371:             ObjectStateManager.ObjectStateManagerChanged -= objectStateManagerObjectStateManagerChanged;

 372:             BulkDeletedEntities.Add(entity);

 373:         }

 374:         #endregion

 375:  

 376:     }

you can add or remove these features according your needs. Here i have tried to provide you some of written code which I think used in more or less in every application.

Repositoty is kind of mediator to connect business layer with data access. So your business layer should be aware of this repository class. I would also like to suggest that You also can do some refactor on my repository class by introducing UoW as umbrella over multiple repositories and shared by all repository in place of the object context directly and method like save ,transaction can be switched over there. Hope its help.Good luck.

kick it on DotNetKicks.com

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

Ado.net Entity Framework Tutorial

Posted on July 14, 2009. Filed under: Entity Framework |


Joydip Kanjilal has released a book on ADO.NET Entity Framework entitled Entity Framework Tutorial .Its a very good thing to jump over the Code on Entity framework and make clear the mechanism concept of EF. I am very thankful to the publication of this book to provide me an opportunity to review this book specially the time when I didn’t find much resource on the framework. It gave me some good idea about Entity Data Model (EDM), using E-SQL and LINQ to Entities to perform queries, and even an introduction to ADO.NET Data Services (Astoria). Entity Framework Tutorial located here. Finally, scripts and code downloads for this book are available here. you can also go to it’s publication site to get more information about this “Good Start” book Here.

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

Implementing Repository Pattern With Entity Framework

Posted on June 10, 2009. Filed under: C#, EF4, Entity Framework | Tags: , , |


Please Note: A Entity Framework- Code-First Version has been written –

https://morshedanwar.wordpress.com/2012/10/03/design-data-access-with-entity-framework-code-first-in-composable-service-end/

Download Source Code: Click here to Get the Sample code of Repository Implementation

Introduction 

 IC340233

The Repository pattern is defined byPatterns of Enterprise Application Architecture as:  

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. 

Repository provides an in-memory like collection interface for accessing domain objects. So as far as the consuming component is concerned, it uses the repository just like a collection when working with Domain objects. The repository then neatly abstracts the internal mechanics of how the Add / Remove calls to the repository translate to the actual data access calls to the data store. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

So with the repository we get a nice abstraction that provides us with persistence ignorance and a nice separation of concerns where the responsibility of persisting domain objects is encapsulated by the Repository leaving the domain objects to deal entirely with the domain model and domain logic.Here are some reasons 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 

Using the Code

Here I use the Composite Repository pattern:

    /// <summary>
    /// Repository Interface defines the base
    /// functionality required by all Repositories.
    /// </summary>
    /// <typeparam name="T">
    /// The entity type that requires a Repository.
    /// </typeparam>
    public interface IRepository<E>
    {
        string KeyProperty { get; set; }

        void Add(E entity);
        void AddOrAttach(E entity);
        void DeleteRelatedEntries(E entity);
        void DeleteRelatedEntries
        (E entity, ObservableCollection<string> keyListOfIgnoreEntites);
        void Delete(E entity);

        ObjectQuery<E> DoQuery();
        ObjectQuery<E> DoQuery(ISpecification<E> where);
        ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex);
        ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression);
        ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression,
                    int maximumRows, int startRowIndex);

        IList<E> SelectAll(string entitySetName);
        IList<E> SelectAll();
        IList<E> SelectAll(string entitySetName, ISpecification<E> where);
        IList<E> SelectAll(ISpecification<E> where);
        IList<E> SelectAll(int maximumRows, int startRowIndex);
        IList<E> SelectAll(Expression<Func<E, object>> sortExpression);
        IList<E> SelectAll(Expression<Func<E, object>> sortExpression,
                    int maximumRows, int startRowIndex);

        E SelectByKey(string Key);

        bool TrySameValueExist(string fieldName, object fieldValue, string key);
        bool TryEntity(ISpecification<E> selectSpec);

        int GetCount();
        int GetCount(ISpecification<E> selectSpec);
    }

 Repository-r

You can write your own Repository for each business object like RoleRepository, UserReporsitory, etc. Or you can implement this interface as a generic class of Repository something like this:  

    public class Repository<E, C> : IRepository<E>
        where E : class
        where C : ObjectContext
    {
        private readonly C _ctx;

        private string _KeyProperty = "ID";

        public string KeyProperty
        {
            get
            {
                return _KeyProperty;
            }
            set
            {
                _KeyProperty = value;
            }
        }

        public C Session
        {
            get { return _ctx; }
        }

        public Repository(C session)
        {
            _ctx = session;
        }

        #region IRepository<E,C> Members

        public int Save()
        {
            return _ctx.SaveChanges();
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        private ObjectQuery<E> DoQuery(string entitySetName)
        {
            return _ctx.CreateQuery<E>("[" + entitySetName + "]");
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public ObjectQuery<E> DoQuery()
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name) + "]");
        }

        /// <summary>
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        private ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
        {
            return
                (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
                .Where(where.EvalPredicate);
        }

        /// <summary>
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public ObjectQuery<E> DoQuery(ISpecification<E> where)
        {
            return
                (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]")
                .Where(where.EvalPredicate);
        }
        /// <summary>
        /// Query Entity with Paging 
        /// </summary>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection of Entities</returns>
        public ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex)
        {
            return (ObjectQuery<E>)_ctx.CreateQuery<E>
        ("[" + this.GetEntitySetName(typeof(E).Name) + "]").Skip<E>(startRowIndex).Take(maximumRows);
        }
        /// <summary>
        /// Query Entity in sorted Order
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="ErrorCode">custom Error Message</param> 
        /// <returns>Collection of Entities</returns>
        public ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression)
        {
            if (null == sortExpression)
            {
                return this.DoQuery();
            }
            return (ObjectQuery<E>)((IRepository<E>)this).DoQuery().OrderBy
                        <E, object>(sortExpression);
        }
        /// <summary>
        /// Query All Entity in sorted Order with Paging support
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection Of entities</returns>
        public ObjectQuery<E> DoQuery(Expression<Func<E, object>>
            sortExpression, int maximumRows, int startRowIndex)
        {
            if (sortExpression == null)
            {
                return ((IRepository<E>)this).DoQuery(maximumRows, startRowIndex);
            }
            return (ObjectQuery<E>)((IRepository<E>)this).DoQuery
            (sortExpression).Skip<E>(startRowIndex).Take(maximumRows);
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll(string entitySetName)
        {
            return DoQuery(entitySetName).ToList();
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll()
        {
            try
            {
                return DoQuery().ToList(); //_ctx.CreateQuery<E>("[" + typeof(E).Name + "]");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll(string entitySetName, ISpecification<E> where)
        {
            return DoQuery(entitySetName, where).ToList();
        }

        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll(ISpecification<E> where)
        {
            return DoQuery(where).ToList();
        }
        /// <summary>
        /// Select All Entity with Paging 
        /// </summary>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection of Entities</returns>
        public IList<E> SelectAll(int maximumRows, int startRowIndex)
        {
            return DoQuery(maximumRows, startRowIndex).ToList();
        }
        /// <summary>
        /// Select All Entity in sorted Order
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="ErrorCode">custom Error Message</param> 
        /// <returns>Collection of Entities</returns>
        public IList<E> SelectAll(Expression<Func<E, object>> sortExpression)
        {
            if (null == sortExpression)
            {
                return DoQuery(sortExpression).ToList();
            }
            return DoQuery(sortExpression).ToList();
        }
        /// <summary>
        /// Select All Entity in sorted Order with Paging support
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection Of entities</returns>
        public IList<E> SelectAll(Expression<Func<E, object>>
            sortExpression, int maximumRows, int startRowIndex)
        {
            if (sortExpression == null)
            {
                return DoQuery(maximumRows, startRowIndex).ToList();
            }
            return DoQuery(sortExpression, maximumRows, startRowIndex).ToList();
        }
        /// <summary>
        /// Get Entity By Primary Key
        /// </summary>
        /// <typeparam name="E">Entity Type</typeparam>
        /// <param name="Key">Primary Key Value</param>
        /// <returns>return entity</returns>
        public E SelectByKey(string Key)
        {
            // First we define the parameter that we are going to use the clause. 
            var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
            MemberExpression leftExpr = MemberExpression.Property(xParam, this._KeyProperty);
            Expression rightExpr = Expression.Constant(Key);
            BinaryExpression binaryExpr = MemberExpression.Equal(leftExpr, rightExpr);
            //Create Lambda Expression for the selection 
            Expression<Func<E, bool>> lambdaExpr =
            Expression.Lambda<Func<E, bool>>(binaryExpr,
            new ParameterExpression[] { xParam });
            //Searching ....
            var resultCollection = (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + this.GetEntitySetName(typeof(E).Name) + "]")
                .Where(lambdaExpr);
            if (null != resultCollection && resultCollection.Count() > 0)
            {
                //return valid single result
                return resultCollection.First<E>();
            }//end if 
            return null;
        }
        /// <summary>
        /// Check if value of specific field is already exist
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="fieldName">name of the Field</param>
        /// <param name="fieldValue">Field value</param>
        /// <param name="key">Primary key value</param>
        /// <returns>True or False</returns>
        public bool TrySameValueExist(string fieldName, object fieldValue, string key)
        {
            // First we define the parameter that we are going to use the clause. 
            var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
            MemberExpression leftExprFieldCheck =
            MemberExpression.Property(xParam, fieldName);
            Expression rightExprFieldCheck = Expression.Constant(fieldValue);
            BinaryExpression binaryExprFieldCheck =
            MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);

            MemberExpression leftExprKeyCheck =
            MemberExpression.Property(xParam, this._KeyProperty);
            Expression rightExprKeyCheck = Expression.Constant(key);
            BinaryExpression binaryExprKeyCheck =
            MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
            BinaryExpression finalBinaryExpr =
            Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);

            //Create Lambda Expression for the selection 
            Expression<Func<E, bool>> lambdaExpr =
            Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
            new ParameterExpression[] { xParam });
            //Searching ....            
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName(typeof(E).Name) + "]").Any<E>
                        (lambdaExpr);
        }
        /// <summary>
        /// Check if Entities exist with Condition
        /// </summary>
        /// <param name="selectExpression">Selection Condition</param>
        /// <returns>True or False</returns>
        public bool TryEntity(ISpecification<E> selectSpec)
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]").Any<E>
                        (selectSpec.EvalPredicate);
        }
        /// <summary>
        /// Get Count of all records
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <returns>count of all records</returns>
        public int GetCount()
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]").Count();
        }
        /// <summary>
        /// Get count of selection
        /// </summary>
        /// <typeparam name="E">Selection Condition</typeparam>
        /// <returns>count of selection</returns>
        public int GetCount(ISpecification<E> selectSpec)
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]")
                .Where(selectSpec.EvalPredicate).Count();
        }
        /// <summary>
        /// Delete data from context
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="entity"></param>
        public void Delete(E entity)
        {
            _ctx.DeleteObject(entity);
        }
        /// <summary>
        /// Delete data from context
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="entity"></param>
        public void Delete(object entity)
        {
            _ctx.DeleteObject(entity);
        }
        /// <summary>
        /// Insert new data into context
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="entity"></param>
        public void Add(E entity)
        {
            _ctx.AddObject( this.GetEntitySetName(entity.GetType().Name), entity);
        }
        /// <summary>
        /// Insert if new otherwise attach data into context
        /// </summary>
        /// <param name="entity"></param>
        public void AddOrAttach(E entity)
        {
            // Define an ObjectStateEntry and EntityKey for the current object.
            EntityKey key;
            object originalItem;
            // Get the detached object's entity key.
            if (((IEntityWithKey)entity).EntityKey == null)
            {
                // Get the entity key of the updated object.
                key = _ctx.CreateEntityKey(this.GetEntitySetName(entity.GetType().Name), entity);
            }
            else
            {
                key = ((IEntityWithKey)entity).EntityKey;
            }
            try
            {
                // Get the original item based on the entity key from the context
                // or from the database.
                if (_ctx.TryGetObjectByKey(key, out originalItem))
                {//accept the changed property
                    if (originalItem is EntityObject &&
                        ((EntityObject)originalItem).EntityState != EntityState.Added)
                    {
                        // Call the ApplyCurrentValues method to apply changes
                        // from the updated item to the original version.
                        _ctx.ApplyCurrentValues(key.EntitySetName, entity);
                    }
                }
                else
                {//add the new entity
                    Add(entity);
                }//end else
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Delete all related entries
        /// </summary>
        /// <param name="entity"></param>        
        public void DeleteRelatedEntries(E entity)
        {
            foreach (var relatedEntity in (((IEntityWithRelationships)entity).
        RelationshipManager.GetAllRelatedEnds().SelectMany(re =>
        re.CreateSourceQuery().OfType<EntityObject>()).Distinct()).ToArray())
            {
                _ctx.DeleteObject(relatedEntity);
            }//end foreach
        }
        /// <summary>
        /// Delete all related entries
        /// </summary>
        /// <param name="entity"></param>        
        public void DeleteRelatedEntries(E entity, ObservableCollection<string>
                            keyListOfIgnoreEntites)
        {
            foreach (var relatedEntity in (((IEntityWithRelationships)entity).
            RelationshipManager.GetAllRelatedEnds().SelectMany(re =>
            re.CreateSourceQuery().OfType<EntityObject>()).Distinct()).ToArray())
            {
                PropertyInfo propInfo = relatedEntity.GetType().GetProperty
                            (this._KeyProperty);
                if (null != propInfo)
                {
                    string value = (string)propInfo.GetValue(relatedEntity, null);
                    if (!string.IsNullOrEmpty(value) &&
                        keyListOfIgnoreEntites.Contains(value))
                    {
                        continue;
                    }//end if 
                }//end if
                _ctx.DeleteObject(relatedEntity);
            }//end foreach
        }

        private string GetEntitySetName(string entityTypeName)
        {
            var container = this._ctx.MetadataWorkspace.GetEntityContainer
                    (this._ctx.DefaultContainerName, DataSpace.CSpace);

            return (from meta in container.BaseEntitySets

                                    where meta.ElementType.Name == entityTypeName

                                    select meta.Name).FirstOrDefault(); 
                     
        }

        #endregion
    }

Notice that, here I also implement IDispose interface to dispose the context manually.To get the name of Entityset, here I have used typeof, but you can do a  metadata query to retrieve the EntitySet name:

container = context.MetadataWorkspace.GetEntityContainer
		(context.DefaultContainerName, DataSpace.CSpace); 

string entitySetName = (from meta in container.BaseEntitySets

                            where meta.ElementType.Name == entityTypeName

                            select meta.Name).FirstOrDefault();   

Here I am not going a little into the code. I have used ObjectQuery to get the features of MargeOption and EnablePlanCaching properties: 

ObjectQuery.EnablePlanCaching Property -indicates whether the query plan should be cached. Plan-caching caches information which is computed as part of putting together the query itself. By caching this, a subsequent execution of the same query (even if you change parameter values) will run much faster than the first one. This information is cached per app-domain so you will generally benefit from the query cache across multiple client requests to the same web app and the like. Here all DoQuery methods are responsible to queries and other query method like SelectAll or Select methods internally use these DoQuery methods with various parameters. 

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. 

SelectByKey is creating a LINQ expression using Expression tree for Primary key and During creating the repository on an entity you can supply this Primary Key property as string. While using the POCO as you entity you can set attribute programming to serve this kind of job.

TrySameValueExist is doing the same job by allow you to set customize field and value comparison there. What will do is create the Expression for you and also add the PrimaryKey comparison so that it excludes the object that you are querying for (and where PrimaryKey != currentObjectPrimaryKey).

Add and Delete method simply respectively call the AddObject and DeleteObject Method of ObjectContext. AddOrAttach method is for special situation where you don’t know whether object is already added or not. It is expensive since it will do query to database to check the existence.

The specification pattern can implement a re-usable business logic component that can be passed around to satisfy certain business criteria. The specification object has a clear and limited responsibility, which can be separated and decoupled from the domain object that uses it. I would highly recommend reading the white paper by Martin Fowler and Eric Evans on the Specification pattern.  

Specification_UML_v2

public interface ISpecification<E>
{
    /// <summary>
    /// Select/Where Expression
    /// </summary>
    Expression<Func<E, bool>> EvalPredicate { get; }
    /// <summary>
    /// Function to evaluate where Expression
    /// </summary>
    Func<E, bool> EvalFunc { get; }
}

You can write your own specification by implementing your interface like RoleSpecification and put down your business logic there. For general use, you can also implement the Interface; such composite specification like this:

    public class Specification<E> : ISpecification<E>
    {
        #region Private Members

        private Func<E, bool> _evalFunc = null;
        private Expression<Func<E, bool>> _evalPredicate;

        #endregion

        #region Virtual Accessors

        public virtual bool Matches(E entity)
        {
            return _evalPredicate.Compile().Invoke(entity);
        }

        public virtual Expression<Func<E, bool>> EvalPredicate
        {
            get { return _evalPredicate; }
        }

        public virtual Func<E, bool> EvalFunc
        {
            get { return _evalPredicate != null ? _evalPredicate.Compile() : null; }
        }

        #endregion

        #region Constructors

        public Specification(Expression<Func<E, bool>> predicate)
        {
            _evalPredicate = predicate;
        }

        private Specification() { }

        #endregion

        #region Private Nested Classes

        private class AndSpecification : Specification<E>
        {
            private readonly ISpecification<E> _left;
            private readonly ISpecification<E> _right;
            public AndSpecification(ISpecification<E> left, ISpecification<E> right)
            {
                this._left = left;
                this._right = right;

                this._evalFunc =
                    (Func<E, bool>)Delegate.Combine
                    (left.EvalPredicate.Compile(),
                    right.EvalPredicate.Compile());

                _evalPredicate = left.EvalPredicate.And(right.EvalPredicate);
            }
            public override bool Matches(E entity)
            {
                return EvalPredicate.Compile().Invoke(entity);
            }
        }

        private class OrSpecification : Specification<E>
        {
            private readonly ISpecification<E> _left;
            private readonly ISpecification<E> _right;
            public OrSpecification(ISpecification<E> left, ISpecification<E> right)
            {
                this._left = left;
                this._right = right;

                this._evalFunc =
                    (Func<E, bool>)Delegate.Combine
                    (left.EvalPredicate.Compile(),
                    right.EvalPredicate.Compile());

                _evalPredicate = left.EvalPredicate.Or(right.EvalPredicate);
            }
            public override bool Matches(E entity)
            {
                return EvalPredicate.Compile().Invoke(entity);
            }
        }

        #endregion

        #region Operator Overloads

        public static Specification<E> operator &(Specification<E> left, ISpecification<E> right)
        {
            return new AndSpecification(left, right);
        }

        public static Specification<E> operator |(Specification<E> left, ISpecification<E> right)
        {
            return new OrSpecification(left, right);
        }

        #endregion

    } 

Here some operator overloading has been applied here is to combine the lambda expression (or predicate) of each specification (left and right side of a composite specification) to create a new lambda expression.This new lambda expression is going to use for querying. So It will help you while you are working with multiple specification already defined as your business rule. 

To use this Repository class from your business layer, one thing I must say that I am sharing a single context in the CRUD process/operation. You must keep alive the context in your entire process of a crud operation. For this, I make a UnitOfWork on top of all.

    public interface IUnitOfWork : IDisposable
    {
        IRepository<TSet> GetRepository<TSet>() where TSet : class;
        DbTransaction BeginTransaction();
        int Save();
    }

Here Unit of Work  has been introduced as umbrella over multiple repositories and shared by all repository in place of the object context directly and method like save, transaction can be switched over there. And here is that implementation.

    public class UnitOfWork<C> : IUnitOfWork where C : ObjectContext
    {
        private DbTransaction _transaction;
        private Dictionary<Type, object> _repositories;
        private C _ctx;

        public UnitOfWork()
        {
            _ctx = Activator.CreateInstance<C>();
            _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 = new Repository<TSet, C>(_ctx);
            _repositories.Add(typeof(TSet), repository);
            return repository;
        }
        /// <summary>
        /// Start Transaction
        /// </summary>
        /// <returns></returns>
        public DbTransaction BeginTransaction()
        {
            if (null == _transaction)
            {
                if (_ctx.Connection.State != ConnectionState.Open)
                {
                    _ctx.Connection.Open();
                }
                this._transaction = _ctx.Connection.BeginTransaction();
            }
            return _transaction;
        }

        public int Save()
        {
            return _ctx.SaveChanges();
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (null != _transaction)
            {
                _transaction.Dispose();
            }

            if (null != _ctx)
            {
                _ctx.Dispose();
            }
        }

        #endregion

    }

Repository is kind of mediator to connect business layer with data access. So your business layer should be aware of this repository class. Now, In my BLLRoleManagement class, Say I have a method to delete User. That will be something like this: 

///  <summary>
/// Delete User
/// </summary>
/// <param name="userID">User ID</param>
/// <returns>True Or False</return>	   
    public static bool DeleteUser(
    string UserID)
        {
            try
            {
                var unitOfWork = new UnitOfWork<ProjectCodeEntities>();
                var userRepository = unitOfWork.GetRepository<User>();
                using (unitOfWork)
                {
                    using (var transaction = unitOfWork.BeginTransaction())
                    {
                        User UserTobeRemoved = userRepository.SelectByKey(UserID);
                        if (UserTobeRemoved == null)
                            return false;

                        userRepository.DeleteRelatedEntries(UserTobeRemoved);
                        userRepository.Delete(UserTobeRemoved);
                        if (unitOfWork.Save() >= 0)
                        {
                            transaction.Commit();
                            return true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ErrorHandler.WrapException(ex, ErrorCodes.DB_DeleteUser_Error_Code);
            }
            return false;
        } 

Here It will initialize a ObjectContext instance which will be used in you entire process/workflow of an operation. For this, I have provided a factory method inside Unit of Work which create a repository for you using that ObjectContext . Delete all related entities associated with User and then delete User. To share the same context in this delete process, I will call this method like this 

new BLLRoleManagement().DeleteUser(userID);   

Surely it has a lot of area to improve. I have used some interfaces here those can be used with dependency injection to decouple your code from EF. Hope its help. So here is my end of the discussion on repository pattern implementation with Entity framework. Good luck. 

References:

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

Entity Framework: Sorting EntitySets According to parent-child relationship using Context properties

Posted on June 5, 2009. Filed under: Entity Framework |


Some month ago while I was working with Entity Framework V1 in data access layer, Client ask me to make a backup of the db  from a generic place. Because client wants a flexibility to use any db storage(MS Sql server ,MySql, may be VistaDB ) with same schema. So I need to make a backup system from a generic place. So what I did (I don’t know it was a good idea or not) was that, I serialized the all the EntitySet graphs and store into some files and then compressed and encrypt them in a single file.  So, when a user wants to restore the DB , I need to  decrypt and de-serialized all the entity objects and attach or add the DB . To attach or Add I have to maintain the order of parent child relationship. So When I store in into File(what I say as Backup) I follow the parent child relationship of the Entity set and maintain the order like – Root First and then The child. During restore I also have to do the reverse way. I mean to say I have attch or add the child first and then the parent. So to do that I have sort the entity set in a parent Child relationship. To make this, first I make a dictionary where I find the references of each entity set. So the key was the Type entity set and value is List of Type of Entity Set those are references the the key Entity set.

/// <summary>
/// Get Current Relationship Dictionary of Entites
/// </summary>
/// <returns>Dictionary of Current Entites</returns>
public static Dictionary<Type, List<Type>> GetCurrentDBEntityRelationshipChildren<T>() where T : ObjectContex
{
    try
    {
        //Take a dictionary
        Dictionary<Type, List<Type>> objectCollection = new Dictionary<Type, List<Type>>();
        //Get al properties contex
        PropertyInfo[] properties = typeof(T).GetProperties();

        foreach (var item in properties)
        {//check 
            //Pickup the properties only those represent entitys of database   
            if (item.PropertyType.BaseType.Equals(typeof(ObjectQuery)))
            {
                //Get Propety value 
                Type entityType = item.PropertyType.GetGenericArguments().First(); ;
                if (null != entityType)
                {
                    try
                    {
                        AddToDictionary(objectCollection, entityType, null);
                        PropertyInfo[] entityProperties = entityType.GetProperties();

                        foreach (var entityProp in entityProperties)
                        {
                            if (entityProp.PropertyType.BaseType.Equals(typeof(RelatedEnd))) //Collection Property
                            {
                                #region Detail Reference - Add on "* to 1" relationship
                                Type detail = entityProp.PropertyType.GetGenericArguments().First();
                                if (null != detail)
                                {
                                    AddToDictionary(objectCollection, entityType, detail);
                                }//end if 
                                #endregion
                            }
                            else if (entityProp.PropertyType.BaseType.Equals(typeof(EntityReference)))
                            {
                                #region Master referance - Add on "1 to 1" relationship
                                bool addInDictionary = false;
                                Type master = entityProp.PropertyType.GetGenericArguments().First();
                                if (null != master)
                                {
                                    try
                                    {
                                        IEnumerable<PropertyInfo> props = master.GetProperties().Where(p => p.PropertyType.BaseType.Equals(typeof(EntityReference)));
                                        if (null != props && props.Count() > 0)
                                        {
                                            foreach (var p in props)
                                            {
                                                if (p.PropertyType.GetGenericArguments().FirstOrDefault().Equals(entityType)
                                                     && p.PropertyType.BaseType.Equals(typeof(EntityReference))
                                                     && !IsInDictionary(objectCollection, master, entityType))
                                                {
                                                    addInDictionary = true;
                                                }
                                            }
                                        }
                                    }
                                    catch (System.Reflection.TargetException ex)
                                    {
                                        throw ErrorHandling.ErrorHandler.WrapException(ex, ErrorCodes.DB_EntityRelationship_Error_Code);
                                    }
                                    catch (TargetInvocationException ex)
                                    {
                                        throw ErrorHandling.ErrorHandler.WrapException(ex, ErrorCodes.DB_EntityRelationship_Error_Code);
                                    }
                                    if (addInDictionary)
                                    {
                                        AddToDictionary(objectCollection, entityType, master);
                                    }
                                }//end if
                                #endregion
                            }
                        }
                    }//end try
                    catch (ReflectionTypeLoadException ex)
                    {
                        throw ErrorHandling.ErrorHandler.WrapException(ex, ErrorCodes.DB_EntityRelationship_Error_Code);
                    }
                    catch (EntityException ex)
                    {
                        throw ErrorHandling.ErrorHandler.WrapException(ex, ErrorCodes.DB_EntityRelationship_Error_Code);
                    }
                }//end if 

            }//end if 
        }//end foreach
        //return the dictionary od priority list
        return objectCollection;
    }
    catch (Exception ex)
    {
        throw ErrorHandling.ErrorHandler.WrapException(ex, ErrorCodes.DB_EntityRelationship_Error_Code);
    }
}

#region Private Methods
/// <summary>
/// Add Value into into Dictionary on checking old data
/// </summary>
/// <param name="Dictionary">Dictionary</param>
/// <param name="Key">Key</param>
/// <param name="Value">Value</param>
private static void AddToDictionary(Dictionary<Type, List<Type>> Dictionary, Type Key, Type Value)
{
    if (Dictionary.ContainsKey(Key))
    {
        List<Type> prevValue = Dictionary[Key] as List<Type>;
        if (prevValue != null && null != Value)
        {
            prevValue.Add(Value);
            Dictionary[Key] = prevValue;
        }//end if 
    }
    else
    {//add new entry with default priority
        if (null != Value)
        {
            Dictionary.Add(Key, new List<Type>() { Value });
        }
        else
        {
            Dictionary.Add(Key, new List<Type>());
        }
    }//end else        
}
/// <summary>
/// Is in dictionary ?
/// </summary>
/// <param name="Dictionary"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
private static bool IsInDictionary(Dictionary<Type, List<Type>> Dictionary, Type key, Type value)
{
    if (Dictionary.ContainsKey(key))
    {
        foreach (var item in (Dictionary[key] as List<Type>))
        {
            if (item.Equals(value))
            {
                return true;
            }
        }
    }
    return false;
}

Now I  was going to  Sort this Using an Sorting algorithm , To make that First I  declared a enum –

[Flags]
public enum EnitySetRefType
{
    None = 0x00,
    ReferencedBy = 0x01,
    RefAndRefBy = 0x02,
    References = 0x04
}

And make a Wrapper class  Of EntitySet Like this –

/// <summary>
/// Wrapper class of EntitySet
/// </summary>
public class EntitySetTypeWrapper
{
    #region Data Members

    // member variables
    public static int MaxLen;
    public static readonly SortedList<string, EntitySetTypeWrapper> sortedList = new SortedList<string, EntitySetTypeWrapper>();

    // Instance variables
    private List<string> _referencedBy = new List<string>();
    private EnitySetRefType _refType;
    private string _name;

    #endregion

    #region Properites

    /// <summary>
    /// Get/Set for ReferencedBy
    /// </summary>
    public List<string> ReferencedBy
    {
        get { return (_referencedBy); }
        set { _referencedBy = value; }
    }

    /// <summary>
    /// Get/Set for RefType
    /// </summary>
    public EnitySetRefType RefType
    {
        get { return (_refType); }
        set { _refType = value; }
    }

    /// <summary>
    /// Get/Set for Name
    /// </summary>
    public string Name
    {
        get { return (_name); }
        set { _name = value; }
    }

    #endregion

    #region Constructor

    /// <summary>
    /// 
    /// </summary>
    /// <param name="row"></param>
    private EntitySetTypeWrapper(string _name)
    {
        Name = _name;
        if (Name.Length > MaxLen)
            MaxLen = Name.Length;

        AddRefType(EnitySetRefType.None);
    }

    #endregion

    #region Methods - Private
    /// <summary>
    /// Private Method
    /// </summary>
    /// <param name="referenceType"></param>
    private void AddRefType(EnitySetRefType referenceType)
    {
        if (_refType == EnitySetRefType.RefAndRefBy)
            return;

        _refType |= referenceType;

        if (_refType == (EnitySetRefType.ReferencedBy | EnitySetRefType.References))
            _refType = EnitySetRefType.RefAndRefBy;
    }
    #endregion
}

Now What I have to do , I should make a Comparer class to sort the entity set Using this wrapper class . So I make a class named EntityInfoComparer  which is inherited from IComparer<EntitySetTypeWrapper>

public class EntityInfoComparer : IComparer<EntitySetTypeWrapper>
{
    /// <summary>
    /// Compare Two Entity
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    public int Compare(EntitySetTypeWrapper x, EntitySetTypeWrapper y)
    {
        // Invalid Data
        if ((x == null) || (y == null))
            throw new ArgumentException("Invalid data type");

        // Objects are the Same
        if (x == y)
        {
            return (0);
        }

        // Sort by Category Number - Object X is in a LOWER Categroy than Object Y
        if (x.RefType < y.RefType)
            return (-1);

        // Sort by Category Number - Object X is in a HIGHER Categroy than Object Y
        if (x.RefType > y.RefType)
            return (1);

        // Sort by Category Number - Object X and Object Y are in the SAME Category
        // Is this a Category 3
        if (x.RefType == EnitySetRefType.RefAndRefBy)
        {
            if (IsInReferenceList(y.Name, x))
            {
                return (-1);
            }

            if (IsInReferenceList(x.Name, y))
            {
                return (1);
            }
        }

        // As a last resort sort by name
        return CompareName(x, y);
    }

    /// <summary>
    /// Compare Two Entity
    /// </summary>
    /// <param name="x">First Entity Defination</param>
    /// <param name="y">Second Entity Defination</param>
    /// <returns></returns>
    private static int CompareName(EntitySetTypeWrapper x, EntitySetTypeWrapper y)
    {
        return (x.Name.CompareTo(y.Name));
    }

    /// <summary>
    /// Is in Reference List?
    /// </summary>
    /// <param name="entityName">Name of Entity</param>
    /// <param name="entityInfo">Entity Defination</param>
    /// <returns></returns>
    private static bool IsInReferenceList(string entityName, EntitySetTypeWrapper entityInfo)
    {
        // Nothing to compare
        if (entityInfo.ReferencedBy.Count == 0)
            return (false);

        // This Entity is Referenced By Entity 
        if (entityInfo.ReferencedBy.Contains(entityName))
            return (true);

        // Recursive call
        foreach (string childEntityName in entityInfo.ReferencedBy)
        {
            // Looking for a child of this entity
            if (IsInReferenceList(entityName, EntitySetTypeWrapper.sortedList[childEntityName]))
                return (true);
        }

        return (false);
    }
}

So , Going to sort using the above dictionary and wrapper class of entity set.So I am going to write an static mathod who First make a dictionary using the method GetCurrentDBEntityRelationshipChildren and then make a SortedList<string, EntitySetTypeWrapper> using the dictionary contents and at the end I sort the SortedList using EntityInfoComparer.

/// <summary>
/// Create a sorted EntitySet list
/// </summary>
/// <returns></returns>
public static List<EntitySetTypeWrapper> CreateSortedEntityListOnChildFirst<T>() where T : ObjectContext
{
    // Retrieve Tables and Foreign Key Relationships
    //DataSet dataSet = GetDatabaseTableInformation();
    Dictionary<Type, List<Type>> entries = GenericDataAccessHelper.GetCurrentDBEntityRelationshipChildren<T>();

    sortedList.Clear();
    // Add All Tables to a List
    foreach (Type key in entries.Keys)
    {
        EntitySetTypeWrapper tableInfo = new EntitySetTypeWrapper(key.Name);
        sortedList.Add(tableInfo.Name, tableInfo);
    }

    // Find Reference Table Information For Each Table
    foreach (var item in entries)
    {
        EntitySetTypeWrapper entityInfo = sortedList[(item.Key as Type).Name];

        List<Type> valueRange = item.Value as List<Type>;
        if (null != valueRange && valueRange.Count > 0 && entityInfo != null)
        {
            foreach (var value in valueRange)
            {
                string childTable = value.Name;

                if (childTable.Equals(entityInfo.Name))
                    continue;

                // Add Child Table to Current Table
                if (!entityInfo.ReferencedBy.Contains(childTable))
                {
                    entityInfo.ReferencedBy.Add(childTable);

                    // Add Reference Type Enumeration For Both Referenced and Referenced By Tables
                    entityInfo.AddRefType(EnitySetRefType.ReferencedBy);
                    sortedList[childTable].AddRefType(EnitySetRefType.References);
                }
            }//end foreach
        }
        // Sort Referenced By Tables By Name
        entityInfo.ReferencedBy.Sort();
    }
    // Perform the Sort with the Custom Sorter
    List<EntitySetTypeWrapper> list = new List<EntitySetTypeWrapper>(sortedList.Values);
    list.Sort(new EntityInfoComparer());

    return (list);
}

That’s the end of  story here…Still its working good with the Domain models of Entity set .  Hope it will help you also. Feel free if you wanna comment on this.

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

Implementing Audit Trail using Entity Framework : Part -2

Posted on April 1, 2009. Filed under: Entity Framework |


DataBaseDbAudit 

DBAuditData

In its part 1, I have talked about creating audit trail objects using the object state entries. As I said before In Second Part I will talk about roll back feature of this audit trail. In first part, I have said that, to get the Rollback feature from that audit trail we have to consider some issues.  We have to maintain the order of entity graph while insertion and deletion. That means root entity has been inserted before the children and during deletion we have to make it reverse. The most important issue is that we have to make sure that each audit trail entry has been inserted according this order. Other issues

1.  We have to maintain insertion date-time to sort the audit trial.

2.  Store the old data and new data.

3.  Store the state the data as audit action.

 

If user chooses a certain time to roll back, we have to start with last audits and do the roll back action for each audit till the certain time. So now the question is arises that what will be roll back action. You can guess that it’s depending on the Audit action of each entity. During rollback –

1.  Inserted data will be deleted.

2.  Deleted data will be inserted.

3.  Modified data will replaced by the old one.

If we see to conceptual model, our Entity set for Audit was-

dbAuditEF

Here “RevisionStamp” holds the audit insertion date-time, Actions holds the audit action of the entity object (Insert/deleted/modified).Other properties describe themselves with their names.

So here “Deleted” and “Modified” object will be roll backed with “Old data” property. And Inserted data will be roll backed with “New data” property.

 UserDateInput

   So First I am going to retrieve all the audits those happen after a specific date-time. This specific date-time is taken from the user. What we have to do, do rollback action for each audit till to specific date and we have to start with last one(bottom  to Top).That is why we are going to sort the audit with “RevisionStamp” in a descending order and start doing rollback-

public static bool RollBack(DateTime rollBackDate, string userName)
{
    using (TransactionScope transactionScope = new TransactionScope(System.Transactions.TransactionScopeOption.Required, TimeSpan.FromMinutes(30)))
    {
        AdventureWorksEntities context = new AdventureWorksEntities();
        try
        {
            if (context.Connection.State != ConnectionState.Open)
            {//Open the connection
                context.Connection.Open();
            }//end if
            //find all audits to roll back
            IEnumerable<DBAudit> auditsToRollBack = DataAdapter.GetEntity<DBAudit>(context, a => a.RevisionStamp >= rollBackDate);

            if (null != auditsToRollBack && auditsToRollBack.Count() > 0)
            {//if any data found to roll back
                //Order by the audits by creational datetime
                IEnumerable<DBAudit> orderedAudits = auditsToRollBack.OrderByDescending(a => a.RevisionStamp);

                foreach (var audit in orderedAudits)
                {//iterate each audit
                    AuditTrailHelper.DoRollBackAction(ref context, audit, userName);
                }//end foreach
                int i = context.SaveChanges();
                if (i > 0)
                {
                    transactionScope.Complete();
                    return true;
                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            // Explicitly dispose of the context,
            // which closes the connection.
            context.Dispose();
        }
    }
    return false;
}

In DoRollBackAction as I said before, “Deleted” and “Modified” object will be roll backed with “Old data” property. And Inserted data will be roll backed with “New data” property.

As I stored old data and new data with XML Serialization while creating Audit object, I have to desterilize those data into “EntityObject” and do the reverse action according to the audit action.

private static void DoRollBackAction(ref AdventureWorksEntities context,  DBAudit auditInfo, string userName)
        {
            if (auditInfo.Actions == AuditTrailHelper.AuditActions.U.ToString() || auditInfo.Actions ==AuditTrailHelper.AuditActions.D.ToString())
            {//For action of Update and Delete
                //Deserialized old data from the XML
                object oldData = AuditTrailHelper.GetAuditObjectFromXML(auditInfo.OldData, auditInfo.TableName);
 
                if (oldData is EntityObject)
                {
                    EntityObject oldEntity = (EntityObject)oldData;
                    oldEntity.EntityKey = null;
                    //add in case of delete or edit the current one with old data
                    DataAdapter.EditEntity(ref context, oldEntity);
                }
            }
            else if (auditInfo.Actions == AuditTrailHelper.AuditActions.I.ToString())
            {//For Insert Action
                object newData = AuditTrailHelper.GetAuditObjectFromXML(auditInfo.NewData, auditInfo.TableName);
 
                if (newData is EntityObject)
                {
                    //Otherwise, delete the entity that has been inserted before
                    EntityObject newEntity = (EntityObject)newData;
                    newEntity.EntityKey = null;
                    EntityKey key = context.CreateEntityKey(newEntity.GetType().Name, newEntity);
 
                    object objToRemoved = null;
                    if (context.TryGetObjectByKey(key, out objToRemoved))
                    {//delete the entity
                        context.DeleteObject(objToRemoved);
                    }//end if
                }
            }
            //delete the audit
            context.DeleteObject(auditInfo);
        }
    }

      

After serializing the entity Object we get the XML string like this –

<SalesOrderDetail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <EntityKey>
    <EntitySetName>SalesOrderDetail</EntitySetName>
    <EntityContainerName>AdventureWorksEntities</EntityContainerName>
  </EntityKey>
  <SalesOrderID>1</SalesOrderID>
  <SalesOrderDetailID>0</SalesOrderDetailID>
  <OrderQty>1</OrderQty>
  <UnitPrice>1898.0944</UnitPrice>
  <UnitPriceDiscount>0</UnitPriceDiscount>
  <LineTotal>0</LineTotal>
  <rowguid>6bfaa372-292a-4fd6-84d3-c4e900f09589</rowguid>
  <ModifiedDate>2009-03-26T16:16:41.9691358+06:00</ModifiedDate>
  <SalesOrderHeaderReference />
  <SpecialOfferProductReference>
    <EntityKey>
      <EntitySetName>SpecialOfferProduct</EntitySetName>
      <EntityContainerName>AdventureWorksEntities</EntityContainerName>
      <EntityKeyValues>
        <EntityKeyMember>
          <Key>SpecialOfferID</Key>
          <Value xsi:type="xsd:int">1</Value>
        </EntityKeyMember>
        <EntityKeyMember>
          <Key>ProductID</Key>
          <Value xsi:type="xsd:int">777</Value>
        </EntityKeyMember>
      </EntityKeyValues>
    </EntityKey>
  </SpecialOfferProductReference>
</SalesOrderDetail>

I have to deserialized this xml string.During deserializing, I have created a XML document and create an XML reader using that document.Using that reader I deserialized the entity object. The method to deserialize the entityobject is –      

public static object GetAuditObjectFromXML(string ObjectInXML, string typeName)
{
    XDocument doc = XDocument.Parse(ObjectInXML);
    //Assuming doc is an XML document containing a serialized object and objType is a System.Type set to the type of the object.
    XmlReader reader = doc.CreateReader();

    Type entityType = Assembly.GetExecutingAssembly().GetType("ImplAuditTrailUsingEF." + typeName);

    XmlSerializer ser = new XmlSerializer(entityType);
    return ser.Deserialize(reader);
}

For inserting deleted object or change back modifed object I wrote a single method to edit the object – “EditEntity(ref AdventureWorksEntities context, EntityObject entity)”.Is this method I change the existance object and attch to the container otherwise I have inserted the entityObject into the context.To save all of this change we must need a entry in Object state cache.

public static void EditEntity(ref AdventureWorksEntities context, EntityObject entity)
{
    // Define an ObjectStateEntry and EntityKey for the current object.
    EntityKey key;
    object originalItem;
    // Get the detached object’s entity key.
    if (entity.EntityKey == null)
    {
        // Get the entity key of the updated object.
        key = context.CreateEntityKey(entity.GetType().Name, entity);
    }
    else
    {
        key = entity.EntityKey;
    }
    try
    {
        // Get the original item based on the entity key from the context
        // or from the database.
        if (context.TryGetObjectByKey(key, out originalItem))
        {//accept the changed property
            // Call the ApplyPropertyChanges method to apply changes
            // from the updated item to the original version.
            context.ApplyPropertyChanges(
                key.EntitySetName, entity);
        }
        else
        {//add the new entity
            context.AddObject(entity.GetType().Name, entity);
        }//end else
    }
    catch (System.Data.MissingPrimaryKeyException ex)
    {
        throw ex;
    }
    catch (System.Data.MappingException ex)
    {
        throw ex;
    }
    catch (System.Data.DataException ex)
    {
        throw ex;
    }
}

Now all we have to do is just call the “RollBack” method and give it a specific date to roll back.

private void btnRollBack_Click(object sender, RoutedEventArgs e)
{
    AuditTrailHelper.RollBack(dtpRollBackDate.SelectedDate.Value, "Admin");
}

That’s all for implementation of Audit trial with entity framework. Here I deleted the audits which has been rolling backed. Definitely each Roll Back operation is also a db operation and I do audit for each roll back operation.

You can get the SourceCode and the article also in codeproject site  -   http://www.codeproject.com/KB/database/ImplAudingTrailUsingEFP-2.aspx

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

Implementing Audit Trail using Ado.net Entity Framework : Part -1

Posted on March 26, 2009. Filed under: Entity Framework |


Please Note: For Audit trail with DbContext (Entity Framework 4.1 and above) I have written about generating Audit log in following article-
http://www.codeproject.com/Articles/363501/Repository-pattern-with-Entity-Framework-Code-Firs
 
https://morshedanwar.wordpress.com/2012/10/03/design-data-access-with-entity-framework-code-first-in-composable-service-end/
 

Entity framework keeps track for those entire objects and relationships which have been deleted, added and modified in the container. EF keeps the state of the object and holds the necessary change-information and all these track information for each object or relationship resides as “objectStateEntry”. Using “ObjectStateManager” one can access all these change-information like object-state (added/modified/deleted), modified properties, original and current values and can easily do audit trail for those objects. To get the Rollback feature from that audit trail we have to consider some issues.  We have to maintain the order of entity graph while insertion and deletion. That means root entity has been inserted before the children and during deletion we have to make it reverse. The most important issue is that we have to make sure that audit trail entry will be inserted according this order.

 Here I am going to talk about audit trail implementation that’s capable to rollback to a certain period. To make such implementation I am going to use the Entity framework’s caching Management that is called as “ObjectStateManager”.Using this Manager I will be capable to find out the object that is currently changed or added or deleted and resides in EF cache as Object state entry. In part 1, I just going to talk about creating audit trail objects using the object state entry. In Second Part I will talk about roll back feature of this audit trial.

 First I make table audit trail in database

 DataBaseDbAudit

For this table, I am going to make an Entity set in my conceptual level as

 dbAuditEF

In Entity Framework, to save my all changes into Db we have call “Context.SaveChanges()” and This Context is a container that has been inherited  from “ObjectContext” Class.To create the Audit trail  Objects for each “Modified/Added/Deleted”I am going to catch the event

Context.SavingChanges +=new EventHandler(Context_SavingChanges);

In sample program I have done it with writing partial class of

public partial class AdventureWorksEntities

    {

partial void OnContextCreated()

        {

            this.SavingChanges += new EventHandler(AdventureWorksEntities_SavingChanges);

        }

        void AdventureWorksEntities_SavingChanges(object sender, EventArgs e)

        {

        }

So in my AdventureWorksEntities_SavingChanges method I am going to create all “dbaudit” objects those are going to save in DB.Here its takes each entry from EF cache of state- Added or Deleted or Modified and call a factory method to produce audit trail object.

    public partial class AdventureWorksEntities

    {

        public string UserName { get; set; }

        List<DBAudit> auditTrailList = new List<DBAudit>();

        public enum AuditActions

        {

            I,

            U,

            D

        }

        partial void OnContextCreated()

        {

            this.SavingChanges += new EventHandler(AdventureWorksEntities_SavingChanges);

        }

        void AdventureWorksEntities_SavingChanges(object sender, EventArgs e)

        {

            IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified);

            foreach (ObjectStateEntry stateEntryEntity in changes)

            {

                if (!stateEntryEntity.IsRelationship &&

                        stateEntryEntity.Entity != null &&

                            !(stateEntryEntity.Entity is DBAudit))

                {//is a normal entry, not a relationship

                    DBAudit audit = this.AuditTrailFactory(stateEntryEntity, UserName);

                    auditTrailList.Add(audit);

                }

            }

            if (auditTrailList.Count > 0)

            {

                foreach (var audit in auditTrailList)

                {//add all audits

                    this.AddToDBAudit(audit);

                }

            }

        }

And here “AuditTrailFactory” is Factory method to create “dbaudit” object. In Entity Framework each entry holds all change definition. Especially for Modify state it keeps the modified properties. I have collected all these properties and serialized them as XML. Using this field you can easily show the changes of modified object with doing any comparison of old and new data. 

        private DBAudit AuditTrailFactory(ObjectStateEntry entry, string UserName)

        {

            DBAudit audit = new DBAudit();

            audit.AuditId = Guid.NewGuid().ToString();

            audit.RevisionStamp = DateTime.Now;

            audit.TableName = entry.EntitySet.Name;

            audit.UserName = UserName;

            if (entry.State == EntityState.Added)

            {//entry is Added

                audit.NewData = GetEntryValueInString(entry, false);

                audit.Actions = AuditActions.I.ToString();

            }

            else if (entry.State == EntityState.Deleted)

            {//entry in deleted

                audit.OldData = GetEntryValueInString(entry, true);

                audit.Actions = AuditActions.D.ToString();

            }

            else

            {//entry is modified

                audit.OldData = GetEntryValueInString(entry, true);

                audit.NewData = GetEntryValueInString(entry, false);

                audit.Actions = AuditActions.U.ToString();

                IEnumerable<string> modifiedProperties = entry.GetModifiedProperties();

                //assing collection of mismatched Columns name as serialized string

                audit.ChangedColumns = XMLSerializationHelper.XmlSerialize(modifiedProperties.ToArray());

            }

            return audit;

        }

Here “GetEntryValueInString” is for creating XML text of Previous or modified object. First I made a clone the current object. Usingentry.GetModifiedProperties()” I can get only modified properties of an object and Using “OriginalValues” and “CurrentValues” I can build myself the old data and new data. Factory has told me what that wants – old or new. At the End I have done XML serialized and return XML string. 

        private string GetEntryValueInString(ObjectStateEntry entry, bool isOrginal)

        {

            if (entry.Entity is EntityObject)

            {

                object target = CloneEntity((EntityObject)entry.Entity);

                foreach (string propName in entry.GetModifiedProperties())

                {

                    object setterValue = null;

                    if (isOrginal)

                    {

                        //Get orginal value

                        setterValue = entry.OriginalValues[propName];

                    }

                    else

                    {

                        //Get orginal value

                        setterValue = entry.CurrentValues[propName];

                    }

                    //Find property to update

                    PropertyInfo propInfo = target.GetType().GetProperty(propName);

                    //update property with orgibal value

                    if (setterValue == DBNull.Value)

                    {//

                        setterValue = null;

                    }

                    propInfo.SetValue(target, setterValue, null);

                }//end foreach

                XmlSerializer formatter = new XmlSerializer(target.GetType());

                XDocument document = new XDocument();

                using (XmlWriter xmlWriter = document.CreateWriter())

                {

                    formatter.Serialize(xmlWriter, target);

                }

                return document.Root.ToString();

            }

            return null;

        }

To clone the entity I have used the method which I have found in MSDN forum Post (Thanx Patrick Magee J )

        public EntityObject CloneEntity(EntityObject obj)

        {

            DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());

            MemoryStream memoryStream = new MemoryStream();

            dcSer.WriteObject(memoryStream, obj);

            memoryStream.Position = 0;

            EntityObject newObject = (EntityObject)dcSer.ReadObject(memoryStream);

            return newObject;

        }

That is all for Part-1 where I just create the Audit trail objects for each CUD operation.

You can get the SourceCode and the article also in codeproject site  –   http://www.codeproject.com/KB/database/ImplAudingTrailUsingEFP1.aspx

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

« Previous Entries

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

%d bloggers like this: