Cómo hacer que el contexto de datos de Entity Framework sea de solo lectura

112

Necesito exponer un contexto de datos de Entity Framework a complementos de terceros. El propósito es permitir que estos complementos obtengan datos únicamente y no permitirles que realicen inserciones, actualizaciones o eliminaciones o cualquier otro comando de modificación de la base de datos. Por lo tanto, ¿cómo puedo hacer que un contexto de datos o una entidad sean de solo lectura?

Harindaka
fuente
3
Bríndeles un contexto con un usuario que no tiene acceso de escritura a la base de datos.
vcsjones
Gracias. Estoy usando una base de datos SQLite. Acabo de descubrir que se puede abrir en modo de solo lectura a través de una opción de cadena de conexión.
Harindaka
2
No les des una DbContext, dales una IQueryableo varias.
ta.speot.is el

Respuestas:

178

Además de conectarse con un usuario de solo lectura, hay algunas otras cosas que puede hacer con su DbContext.

public class MyReadOnlyContext : DbContext
{
    // Use ReadOnlyConnectionString from App/Web.config
    public MyContext()
        : base("Name=ReadOnlyConnectionString")
    {
    }

    // Don't expose Add(), Remove(), etc.
    public DbQuery<Customer> Customers
    {
        get
        {
            // Don't track changes to query results
            return Set<Customer>().AsNoTracking();
        }
    }

    public override int SaveChanges()
    {
        // Throw if they try to call this
        throw new InvalidOperationException("This context is read-only.");
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Need this since there is no DbSet<Customer> property
        modelBuilder.Entity<Customer>();
    }
}
bricelam
fuente
1
era obvio que eres un 'hombre interior' :) - esto es mucho más interesante que una conexión 'sólo lectura'
NSGaga-sobre todo-inactiva
6
Tenga en cuenta que el uso AsNoTracking()hará que sea imposible usar la carga diferida.
Tom Pažourek
@ TomPažourek No sé si eso es cierto ... Creo que EF todavía crea proxies de carga diferida, pero la resolución de identidad puede volverse un poco extraña.
bricelam
3
No olvide anular public override Task<int> SaveChangesAsync()también.
Pete
7
No confíe en esto, porque (context as IObjectContextAdapter).ObjectContext.SaveChanges()seguirá funcionando. La mejor opción es usar el DbContext(string nameOrConnectionString);constructor con una cadena de conexión de lectura / escritura para la creación de bases de datos y una cadena de conexión de solo lectura después.
Jürgen Steinblock
32

A diferencia de la respuesta aceptada, creo que sería mejor favorecer la composición sobre la herencia . Entonces no habría necesidad de mantener métodos como SaveChanges para lanzar una excepción. Además, ¿por qué necesita tener tales métodos en primer lugar? Debe diseñar una clase de manera que su consumidor no se deje engañar cuando mire su lista de métodos. La interfaz pública debe estar alineada con la intención y el objetivo reales de la clase, mientras que en la respuesta aceptada tener SaveChanges no implica que el contexto sea de solo lectura.

En lugares donde necesito tener un contexto de solo lectura, como en el lado de lectura del patrón CQRS , utilizo la siguiente implementación. No proporciona nada más que funciones de consulta a su consumidor.

public class ReadOnlyDataContext
{
    private readonly DbContext _dbContext;

    public ReadOnlyDataContext(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IQueryable<TEntity> Set<TEntity>() where TEntity : class
    {
        return _dbContext.Set<TEntity>().AsNoTracking();
    }
}

Al usar ReadOnlyDataContext, puede tener acceso solo a las capacidades de consulta de DbContext. Digamos que tiene una entidad llamada Order, entonces usaría la instancia ReadOnlyDataContext de la siguiente manera.

readOnlyDataContext.Set<Order>().Where(q=> q.Status==OrderStatus.Delivered).ToArray();
Ehsan Mirsaeedi
fuente
¿Este método permite el uso de un db_datareader solo inicio de sesión sql? Con un DBContext estándar, EF lanza el permiso CREATE TABLE denegado incluso cuando mi código de consulta no incluye ningún SaveChanges ().
llegando
2
Y haz que herede deIDisposable
hkarask
En lugar de usar Set <>, sugeriría Query <>. public IQueryable<TEntity> Get<TEntity>() where TEntity : class { return _dbContext.Query<TEntity>().AsNoTracking(); }
Allan Nielsen
@hkarask - no estoy seguro de si haría eso. Dado que esta llamada no creó el DbContext, no debería eliminarlo. Esto podría dar lugar a algunos errores difíciles de rastrear más adelante.
Allan Nielsen
@AllanNielsen Query <> está marcado como obsoleto. De acuerdo con esto, debe usarse Set <>.
Frank