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.


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

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.