Як зробити контент контексту даних 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
було очевидно, що ти "всередині людини" :) - це набагато цікавіше, ніж зв'язок "лише для читання"
NSGaga - здебільшого неактивний

6
Зауважте, що використання AsNoTracking()унеможливить використання лінивого завантаження.
Том Пажурек

@ TomPažourek Я не знаю, чи це правда ... Я думаю, що EF все ще створює проксі-леді, але дозвіл ідентичності може стати трохи дивним.
бричелам

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

7
Не покладайтеся на це, бо (context as IObjectContextAdapter).ObjectContext.SaveChanges()все одно буде працювати. Найкращим вибором є використання DbContext(string nameOrConnectionString);конструктора зі зв’язком для читання / запису для створення баз даних та послідовного з'єднання для читання.
Юрген Штейнблок

33

На відміну від прийнятої відповіді, я вважаю, що було б краще віддати перевагу композиції над спадщиною . Тоді не потрібно буде зберігати такі методи, як SaveChanges, щоб кинути виняток. Більше того, навіщо вам потрібно мати такі методи в першу чергу? Ви повинні розробити клас таким чином, щоб його споживач не обдурив, коли він переглядає його список методів. Публічний інтерфейс повинен відповідати фактичному наміру та цілі класу, тоді як у прийнятій відповіді, що містить SaveChanges, не випливає, що Контекст є лише для читання.

У тих місцях, де мені потрібно мати контекст, доступний лише для читання, наприклад, на стороні читання шаблону 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. Скажімо, у вас є сутність з ім'ям Порядок, тоді ви використовували б екземпляр ReadOnlyDataContext таким чином, як нижче.

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

Чи дозволяє цей метод використовувати для входу db_datareader тільки sql? За допомогою стандартного DBContext EF видає CREATE TABLE дозвіл, відхилений навіть тоді, коли мій код запиту не містить SaveChanges ().
досягненнянексу

2
І зробіть це у спадок відIDisposable
hkarask

Замість використання Set <> я б запропонував Запит <>. 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.