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

Advertisements
Read Full Post | Make a Comment ( 5 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 )

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

%d bloggers like this: