Как сделать контекст данных Entity Framework только для чтения


112

Мне нужно предоставить доступ к контексту данных Entity Framework сторонним плагинам. Цель состоит в том, чтобы позволить этим плагинам получать только данные и не позволять им выполнять вставку, обновление или удаление или любые другие команды модификации базы данных. Следовательно, как я могу сделать контекст данных или сущность только для чтения.


3
Дайте им контекст с пользователем, у которого нет доступа на запись в базу данных.
vcsjones

Спасибо. Я использую базу данных SQLite. Только что выяснил, что его можно открыть в режиме только для чтения с помощью параметра строки подключения.
Хариндака

2
Не давайте им DbContext, дайте им IQueryableнесколько.
ta.speot.is

Ответы:


178

Помимо подключения к пользователю, имеющему доступ только для чтения, есть еще несколько вещей, которые вы можете сделать со своим 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
было очевидно, что вы «внутренний человек» :) - это намного интереснее, чем соединение «только для
чтения»

6
Обратите внимание, что использование AsNoTracking()делает невозможным использование отложенной загрузки.
Том Пажурек

@ TomPažourek Я не знаю, правда ли это ... Я думаю, что EF по-прежнему создает прокси с отложенной загрузкой, но разрешение идентификации может быть немного странным.
bricelam

3
Не забудьте также переопределить public override Task<int> SaveChangesAsync().
Пит

7
Не надейтесь на это, потому что (context as IObjectContextAdapter).ObjectContext.SaveChanges()все равно будет работать. Лучшим выбором является использование DbContext(string nameOrConnectionString);конструктора со строкой подключения для чтения / записи для создания базы данных и строкой подключения только для чтения впоследствии.
Юрген Штайнблок

33

В отличие от принятого ответа, я считаю, что было бы лучше отдать предпочтение композиции, а не наследованию . Тогда не было бы необходимости хранить такие методы, как SaveChanges, для создания исключения. Более того, зачем вообще нужны такие методы? Вы должны спроектировать класс таким образом, чтобы его потребителя не обманули, когда он посмотрит на его список методов. Открытый интерфейс должен соответствовать фактическим намерениям и цели класса, тогда как в принятом ответе наличие SaveChanges не означает, что Context доступен только для чтения.

В тех местах, где мне нужен контекст только для чтения, например на стороне чтения шаблона CQRS , я использую следующую реализацию. Он не предоставляет своим потребителям ничего, кроме возможностей запросов.

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

Используя ReadOnlyDataContext, вы можете получить доступ только к возможностям запросов DbContext. Допустим, у вас есть объект с именем Order, тогда вы должны использовать экземпляр ReadOnlyDataContext, как показано ниже.

readOnlyDataContext.Set<Order>().Where(q=> q.Status==OrderStatus.Delivered).ToArray();

Позволяет ли этот метод использовать только sql-логин db_datareader? При использовании стандартного DBContext EF выдает отказ в разрешении CREATE TABLE, даже если мой код запроса не включает никаких SaveChanges ().
достижениеnexus

2
И сделать это в наследство отIDisposable
hkarask

Вместо использования Set <> я бы предложил Query <>. public IQueryable<TEntity> Get<TEntity>() where TEntity : class { return _dbContext.Query<TEntity>().AsNoTracking(); }
Аллан Нильсен

@hkarask - не уверен, что сделаю это. Поскольку этот вызов не создавал DbContext, его не следует удалять. Это может привести к тому, что в дальнейшем будет сложно отслеживать ошибки.
Аллан Нильсен

@AllanNielsen Query <> помечен как устаревший. В соответствии с ним следует использовать Set <>.
Фрэнк
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.