0

I've read that Dapper is much faster than EF generally and was thinking about using Dapper for the query side and EF for the write side of an application using the CQRS (lite) pattern.

However, I'm also aware that EF has the ability to switch off tracking by default. Is it worth creating 2 database contexts, one for reading with AsNoTracking enabled across all entities, and one for writing with tracking enabled? This way I have the performance benefit without using another library and adding extra complexity.

user183872
  • 745
  • 2
  • 5
  • 18

1 Answers1

2

As for Entity Framework, you should only have one DbContext in your application.

You can consider using a simple abstraction of your context to give you a read side and a write side:

public abstract class Entity
{
    // Marker class
}

public interface IEntityReader<out TEntity> where TEntity : Entity
{
    IQueryable<TEntity> Query();
}

public interface IEntityWriter<TEntity> where TEntity : Entity
{
    TEntity Get(object primaryKey);

    void Save(TEntity entity);

    void Delete(TEntity entity);
}

With the implementation fo these like:

internal sealed class EntityFrameworkReader<TEntity> : IEntityReader<TEntity> where TEntity : Entity
{
    private readonly Func<DbContext> _contextProvider;

    public EntityFrameworkReader(Func<DbContext> contextProvider)
    {
        _contextProvider = contextProvider;
    }

    public IQueryable<TEntity> Query()
    {
        return _contextProvider().Set<TEntity>().AsNoTracking();
    }

}

internal sealed class EntityFrameworkWriter<TEntity> : IEntityWriter<TEntity> where TEntity : Entity
{
    private readonly Func<DbContext> _contextProvider;

    public EntityFrameworkWriter(Func<DbContext> contextProvider)
    {
        _contextProvider = contextProvider;
    }

    public void Save(TEntity entity)
    {
        var context = _contextProvider();
        var entry = context.Entry(entity);

        // If it is not tracked by the context, add it to the context
        if (entry.State == EntityState.Detached)
        {
            // This also sets the entity state to added.
            context.Set<TEntity>().Add(entity);
        }
        else
        {
            // Tells the context that the entity should be updated during saving changes
            entry.State = EntityState.Modified;
        }

        // In a perfect world, you should use a decorator and save the changes there using Dependency Injection
        context.SaveChanges();
    }

    public void Delete(TEntity entity)
    {
        var context = _contextProvider();
        var entry = context.Entry(entity);
        if (entry.State != EntityState.Deleted)
        {
            // This also sets the entity state to Deleted.
            context.Set<TEntity>().Remove(entity);
        }

        // In a perfect world, you should use a decorator and save the changes there using Dependency Injection
        context.SaveChanges();
    }

    public TEntity Get(object primaryKey)
    {
        var context = _contextProvider();
        var entity = context.Set<TEntity>().Find(primaryKey);
        if (entity == null) return null;

        // We found the entity, set the state to unchanged.
        context.Entry(entity).State = EntityState.Unchanged;

        return entity;
    }
}

If you are using a dependency injection library, like Simple Injector, you can wire it up like so:

container.Register<DbContext, MyDbContext>(Lifestyle.Scoped);
container.Register(typeof(IEntityWriter<>), typeof(EntityFrameworkWriter<>), Lifestyle.Scoped);
container.Register(typeof(IEntityReader<>), typeof(EntityFrameworkReader<>), Lifestyle.Scoped);
janhartmann
  • 13,440
  • 13
  • 78
  • 130
  • This looks great! So a specific Repository would either inherit from a Writer or Reader which then provides the correct functionality? – user183872 Mar 23 '16 at 11:43
  • You can just use `IEntityWriter` when ever you need to write entities. No need for a specific repository :-) – janhartmann Mar 23 '16 at 12:20
  • "As for Entity Framework, you should only have one DbContext in your application." Is there any reason for that? – romanoza May 27 '21 at 06:45