Como tornar o Entity Framework Data Context Readonly

112

Eu preciso expor um Entity Framework Data Context para plug-ins de terceiros. O objetivo é permitir que esses plug-ins busquem apenas dados e não permitir que eles façam inserções, atualizações ou exclusões ou quaisquer outros comandos de modificação do banco de dados. Portanto, como posso tornar um contexto de dados ou entidade somente leitura.

Harindaka
fonte
3
Dê a eles um contexto com um usuário que não tem acesso de gravação ao banco de dados.
vcsjones
Obrigado. Estou usando um banco de dados SQLite. Acabei de descobrir que ele pode ser aberto no modo somente leitura por meio de uma opção de string de conexão.
Harindaka
2
Não dê a eles um DbContext, dê-lhes um IQueryableou vários.
ta.speot.is

Respostas:

178

Além de conectar-se a um usuário somente leitura, existem algumas outras coisas que você pode fazer no seu 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
fonte
1
era óbvio que você é um 'homem de dentro' :) - isso é muito mais interessante do que uma conexão 'somente leitura'
NSGaga-principalmente-inativa
6
Observe que o uso AsNoTracking()tornará impossível o carregamento lento.
Tom Pažourek de
@ TomPažourek Não sei se isso é verdade ... Acho que EF ainda cria proxies de carregamento lento, mas a resolução de identidade pode ficar um pouco estranha.
bricelam
3
Não se esqueça de substituir public override Task<int> SaveChangesAsync()também.
Pete
7
Não confie nisso, porque (context as IObjectContextAdapter).ObjectContext.SaveChanges()ainda funcionará. A melhor escolha é usar o DbContext(string nameOrConnectionString);contstructor com uma cadeia de conexão de leitura / gravação para a criação de banco de dados e uma cadeia de conexão somente leitura posteriormente.
Jürgen Steinblock
33

Ao contrário da resposta aceita, acredito que seria melhor favorecer a composição em vez da herança . Então, não haveria necessidade de manter métodos como SaveChanges para lançar uma exceção. Além disso, por que você precisa ter esses métodos em primeiro lugar? Você deve projetar uma classe de forma que seu consumidor não seja enganado ao olhar sua lista de métodos. A interface pública deve estar alinhada com a intenção real e objetivo da classe, enquanto a resposta aceita ter SaveChanges não significa que o Contexto é somente leitura.

Em lugares onde preciso ter um contexto somente leitura, como no lado Leitura do padrão CQRS , uso a implementação a seguir. Ele não fornece nada além de recursos de consulta para seu 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();
    }
}

Usando ReadOnlyDataContext, você pode ter acesso apenas a recursos de consulta de DbContext. Digamos que você tenha uma entidade chamada Order, então você usaria a instância ReadOnlyDataContext da maneira abaixo.

readOnlyDataContext.Set<Order>().Where(q=> q.Status==OrderStatus.Delivered).ToArray();
Ehsan Mirsaeedi
fonte
Este método permite o uso de um login db_datareader apenas sql? Com um DBContext padrão, EF lança a permissão CREATE TABLE negada mesmo quando meu código de consulta não inclui nenhum SaveChanges ().
alcançando o
2
E faça com que ele herde deIDisposable
hkarask
Em vez de usar Set <>, sugiro Query <>. public IQueryable<TEntity> Get<TEntity>() where TEntity : class { return _dbContext.Query<TEntity>().AsNoTracking(); }
Allan Nielsen
@hkarask - não tenho certeza se faria isso. Como essa chamada não criou o DbContext, ela não deve descartá-lo. Isso pode levar a alguns bugs difíceis de rastrear mais tarde.
Allan Nielsen
@AllanNielsen Query <> está marcada como obsoleta. De acordo com ele, Set <> deve ser usado.
Frank