Unit of Work Pattern And Updating Entity Collection Properties

c# ef-code-first entity-framework-6 unit-of-work

Question

I'm using generic repository pattern with unit of work implementation for my project.

Recently I've come to an issue which I could not solve. When I try to update an entity's collection property (i.e: Add a new associated entity) and call update on my UoW (to delegate it to repository and obviously EF) it does not save to the database.

My generic repository:

public class GenericRepository<TEntity> where TEntity : class
    {
        internal MyDBContext context;
        internal DbSet<TEntity> dbSet;

        public GenericRepository(MyDBContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }

        internal virtual IQueryable<TEntity> BuildQuery(Expression<Func<TEntity,bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet.AsNoTracking();
            foreach (var include in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(include);
            }

            if (filter != null)
                query = query.Where(filter);

            if (orderBy != null)
                return orderBy(query);

            return query;
        }

        public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
        {
            IQueryable<TEntity> query = BuildQuery(filter, orderBy, includeProperties);
            return query.ToList();
        }

    public virtual void Update(TEntity entity)
    {
        dbSet.Attach(entity);
        context.Entry<TEntity>(entity).State = EntityState.Modified;
    }
}

My Unit of Work implementation

public class UnitOfWork : IDisposable
{
    //code removed for clarity

    public GenericRepository<CorporateServiceCategory> ServiceCategories
    {
        get
        {
            if(this.serviceCategoryRepository == null)
            {
                serviceCategoryRepository = new GenericRepository<CorporateServiceCategory>(context);
            }

            return serviceCategoryRepository;
        }
    }

    public void Commit()
    {
       context.SaveChanges();
    }
}

What I'm trying to do is:

using(var unit = new UnitOfwork())
{
    //unit.Companies is a generic repository instance for Company entities.
    var company = unit.Companies.Get(filter: f => f.Id == 1).SingleOrDefault();

    company.ServiceCategories.Add(new ServiceCategory {Name = "Demo"});
    unit.Companies.Update(company);

    //This is a call to context.SaveChanges();
    unit.Commit();
}

I expect this code to create a new Company -> ServiceCategory association and add a record to the database. When I do the same operation without Unit of Work but using DbContext itself, it works.

What am I doing wrong with my UoW & Generic Repository implementation?

1
0
3/4/2015 1:59:26 PM

Accepted Answer

Thanks to SOfanatic's comment, the problem has solved now.

I've updated my GenericRepository's BuildQuery method to reflect SOfanatic's suggestion and it worked.

Here is the updated BuildQuery method:

internal virtual IQueryable<TEntity> BuildQuery(Expression<Func<TEntity,bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
        {
            IQueryable<TEntity> query = this.context.IsReadOnly ? dbSet.AsNoTracking() : dbSet;
            foreach (var include in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(include);
            }

            if (filter != null)
                query = query.Where(filter);

            if (orderBy != null)
                return orderBy(query);

            return query;
        }

DbContext.IsReadOnly is a custom property I added to my DbContext implementation. That way if I want to load entities in a kind of "read-only" mode (only selects) I disable lazy loading, proxy generation and change tracking so it increases EF performance a bit.

0
3/4/2015 2:45:36 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow