Modos de lectura y escritura EF para CQRS

.net cqrs dapper entity-framework

Pregunta

He leído que Dapper es mucho más rápido que EF en general y estaba pensando en usar Dapper para el lado de la consulta y EF para el lado de escritura de una aplicación que usa el patrón CQRS (lite).

Sin embargo, también soy consciente de que EF tiene la capacidad de desactivar el seguimiento por defecto. ¿Vale la pena crear 2 contextos de base de datos, uno para leer con AsNoTracking habilitado en todas las entidades, y otro para escribir con seguimiento habilitado? De esta forma tengo el beneficio de rendimiento sin usar otra biblioteca y agregando complejidad extra.

Respuesta aceptada

En cuanto a Entity Framework, solo debe tener un DbContext en su aplicación.

Puede considerar usar una abstracción simple de su contexto para darle un lado de lectura y otro de escritura:

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

Con la implementación para estos como:

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

Si está utilizando una biblioteca de inyección de dependencias, como Simple Injector, puede conectarlo de la siguiente manera:

container.Register<DbContext, MyDbContext>(Lifestyle.Scoped);
container.Register(typeof(IEntityWriter<>), typeof(EntityFrameworkWriter<>), Lifestyle.Scoped);
container.Register(typeof(IEntityReader<>), typeof(EntityFrameworkReader<>), Lifestyle.Scoped);


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué