Comment créer un contexte de données Entity Framework en lecture seule

112

J'ai besoin d'exposer un contexte de données Entity Framework à des plugins tiers. Le but est de permettre à ces plugins de récupérer uniquement des données et de ne pas les laisser émettre des insertions, des mises à jour ou des suppressions ou toute autre commande de modification de base de données. Par conséquent, comment puis-je créer un contexte de données ou une entité en lecture seule.

Harindaka
la source
3
Donnez-leur un contexte avec un utilisateur qui n'a pas d'accès en écriture à la base de données.
vcsjones
Merci. J'utilise une base de données SQLite. Je viens de découvrir qu'il peut être ouvert en mode lecture seule via une option de chaîne de connexion.
Harindaka
2
Ne leur donnez pas un DbContext, donnez-leur un IQueryableou plusieurs.
ta.speot.is

Réponses:

178

En plus de vous connecter avec un utilisateur en lecture seule, vous pouvez effectuer d'autres opérations sur votre 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
la source
1
il était évident que vous êtes un `` homme intérieur '' :) - c'est beaucoup plus intéressant qu'une connexion `` lecture seule ''
NSGaga-la plupart du temps-inactif
6
Notez que l'utilisation AsNoTracking()rendra impossible l'utilisation du chargement différé.
Tom Pažourek
@ TomPažourek Je ne sais pas si c'est vrai ... Je pense qu'EF crée toujours des proxys à chargement paresseux, mais la résolution d'identité peut devenir un peu bizarre.
bricelam
3
N'oubliez pas de remplacer public override Task<int> SaveChangesAsync()également.
Pete
7
Ne comptez pas là-dessus, car (context as IObjectContextAdapter).ObjectContext.SaveChanges()cela fonctionnera toujours. Le meilleur choix est d'utiliser le DbContext(string nameOrConnectionString);contstructor avec une chaîne de connexion en lecture / écriture pour la création de base de données et une chaîne de connexion en lecture seule par la suite.
Jürgen Steinblock
33

Contrairement à la réponse acceptée, je pense qu'il vaudrait mieux favoriser la composition plutôt que l'héritage . Ensuite, il n'y aurait pas besoin de conserver des méthodes telles que SaveChanges pour lever une exception. De plus, pourquoi avez-vous besoin de telles méthodes en premier lieu? Vous devez concevoir une classe de manière à ce que son consommateur ne soit pas dupe lorsqu'il regarde sa liste de méthodes. L'interface publique doit être alignée sur l'intention et l'objectif réels de la classe, tandis que dans la réponse acceptée, le fait d'avoir SaveChanges n'implique pas que le contexte est en lecture seule.

Dans les endroits où j'ai besoin d'un contexte en lecture seule, comme dans le côté lecture du modèle CQRS , j'utilise l'implémentation suivante. Il ne fournit rien d'autre que des capacités d'interrogation à son consommateur.

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

En utilisant ReadOnlyDataContext, vous pouvez avoir accès uniquement aux fonctionnalités d'interrogation de DbContext. Supposons que vous ayez une entité nommée Order, alors vous utiliseriez l'instance ReadOnlyDataContext d'une manière comme ci-dessous.

readOnlyDataContext.Set<Order>().Where(q=> q.Status==OrderStatus.Delivered).ToArray();
Ehsan Mirsaeedi
la source
Cette méthode autorise-t-elle l'utilisation d'une connexion SQL db_datareader uniquement? Avec un EF DBContext standard, l'autorisation CREATE TABLE est refusée même lorsque mon code de requête n'inclut aucun SaveChanges ().
atteignantnexus
2
Et faites-en hériter deIDisposable
hkarask
Au lieu d'utiliser Set <>, je suggérerais Query <>. public IQueryable<TEntity> Get<TEntity>() where TEntity : class { return _dbContext.Query<TEntity>().AsNoTracking(); }
Allan Nielsen
@hkarask - je ne suis pas sûr de le faire. Étant donné que cet appel n'a pas créé le DbContext, il ne doit pas le supprimer. Cela pourrait conduire à des bogues difficiles à localiser plus tard.
Allan Nielsen
@AllanNielsen Query <> est marqué comme obsolète. Selon lui, Set <> doit être utilisé.
Frank le