Unit of Work Pattern And Updating Entity Collection Properties

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

Question

For my project, I'm implementing the generic repository pattern with units of labor.

Recently, I encountered a problem that I was unable to resolve. The database is not saved when I attempt to update an entity's collection property (i.e., add a new associated entity) and call update on my UoW (to delegate it to the repository and presumably EF).

My broad 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;
    }
}

Implementation of my Unit of Work

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 attempting 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 anticipate that this code will add a record to the database and establish a new Company -> ServiceCategory association. The identical operation works when I use DbContext by itself rather than a Unit of Work.

What am I doing incorrectly with my implementation of the UoW and Generic Repository?

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

Accepted Answer

Since SOfanatic's comment, the issue has been resolved.

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

Here is the most recent BuildQuery technique:

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;
        }

I created a special property called DbContext.IsReadOnly to my DbContext implementation. In this approach, I may turn off lazy loading, proxy creation, and change tracking to somewhat improve EF performance if I wish to load entities in a sort of "read-only" mode (just selects).

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