Jak sprawić, by kontekst danych Entity Framework był tylko do odczytu

112

Muszę ujawnić kontekst danych Entity Framework wtyczkom innych firm. Celem jest zezwolenie tym wtyczkom na pobieranie tylko danych, a nie pozwolenie im na wstawianie, aktualizowanie lub usuwanie lub jakiekolwiek inne polecenia modyfikacji bazy danych. Dlatego jak mogę uczynić kontekst danych lub jednostkę tylko do odczytu.

Harindaka
źródło
3
Daj im kontekst z użytkownikiem, który nie ma dostępu do zapisu w bazie danych.
vcsjones
Dzięki. Używam bazy danych SQLite. Właśnie dowiedziałem się, że można go otworzyć w trybie tylko do odczytu za pomocą opcji parametrów połączenia.
Harindaka
2
Nie dawaj im DbContext, daj im jedną IQueryablelub kilka.
ta.speot.is

Odpowiedzi:

178

Oprócz łączenia się z użytkownikiem tylko do odczytu, istnieje kilka innych rzeczy, które możesz zrobić z 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
źródło
1
było oczywiste, że jesteś „człowiekiem wewnętrznym” :) - to jest o wiele bardziej interesujące niż połączenie „tylko do odczytu”
NSGaga-głównie-nieaktywne
6
Zauważ, że użycie AsNoTracking()uniemożliwi korzystanie z leniwego ładowania.
Tom Pažourek
@ TomPažourek Nie wiem, czy to prawda ... Myślę, że EF nadal tworzy leniwie ładujące się serwery proxy, ale rozpoznawanie tożsamości może być trochę dziwne.
bricelam
3
Nie zapomnij też o nadpisaniu public override Task<int> SaveChangesAsync().
Pete
7
Nie polegaj na tym, ponieważ (context as IObjectContextAdapter).ObjectContext.SaveChanges()nadal będzie działać. Najlepszym wyborem jest użycie DbContext(string nameOrConnectionString);konstruktora z ciągiem połączenia do odczytu / zapisu do tworzenia bazy danych, a następnie ciągiem połączenia tylko do odczytu.
Jürgen Steinblock
33

W przeciwieństwie do przyjętej odpowiedzi, uważam, że lepiej byłoby przedkładać kompozycję nad dziedziczenie . Wtedy nie byłoby potrzeby utrzymywania metod, takich jak SaveChanges, aby zgłosić wyjątek. Poza tym, po co w ogóle trzeba mieć takie metody? Należy zaprojektować klasę w taki sposób, aby jej konsument nie dał się zwieść, gdy spojrzy na listę metod. Interfejs publiczny powinien być zgodny z rzeczywistym zamiarem i celem klasy, podczas gdy w zaakceptowanej odpowiedzi posiadanie SaveChanges nie oznacza, że ​​Context jest tylko do odczytu.

W miejscach, w których muszę mieć kontekst tylko do odczytu, na przykład po stronie odczytu wzorca CQRS , używam następującej implementacji. Nie zapewnia niczego poza możliwościami zapytań dla swojego konsumenta.

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

Korzystając z ReadOnlyDataContext, można mieć dostęp tylko do możliwości wykonywania zapytań dotyczących DbContext. Załóżmy, że masz jednostkę o nazwie Order, a następnie użyjesz instancji ReadOnlyDataContext w sposób jak poniżej.

readOnlyDataContext.Set<Order>().Where(q=> q.Status==OrderStatus.Delivered).ToArray();
Ehsan Mirsaeedi
źródło
Czy ta metoda pozwala na używanie tylko logowania przez db_datareader? Ze standardowym DBContext EF zgłasza odmowę uprawnienia CREATE TABLE, nawet jeśli mój kod zapytania nie zawiera żadnych SaveChanges ().
dotarcie do
2
I niech odziedziczy poIDisposable
hkarask
Zamiast używać Set <>, sugerowałbym Query <>. public IQueryable<TEntity> Get<TEntity>() where TEntity : class { return _dbContext.Query<TEntity>().AsNoTracking(); }
Allan Nielsen
@hkarask - nie jestem pewien, czy to zrobię. Ponieważ to wywołanie nie utworzyło DbContext, nie powinno go usuwać. Może to prowadzić do trudnych do późniejszego wyśledzenia błędów.
Allan Nielsen
Zapytanie @AllanNielsen <> jest oznaczone jako przestarzałe. Zgodnie z nim należy użyć Set <>.
Frank