Reputation: 767
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.
Upvotes: 0
Views: 2748
Reputation: 15003
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);
Upvotes: 2