Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Entity Framework Data Context Readonly

People also ask

Should DbContext be scoped or transient?

But if you use other injected services, with "transient" on the DBContext, every service gets his own instance of it. In order to avoid that you should always use "scoped" on the DBContext. Correct. Maybe there are cases in which you need a transient EF-Context - but usually you should stick to scoped.

Does DbContext need to be disposed?

Don't dispose DbContext objects. Although the DbContext implements IDisposable , you shouldn't manually dispose it, nor should you wrap it in a using statement. DbContext manages its own lifetime; when your data access request is completed, DbContext will automatically close the database connection for you.

Should DbContext be Singleton or transient?

DbContext should not be used as a singleton because it is holding a connection object which cannot be used by multiple threads at the same time.

Why is DbContext not thread-safe?

This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. When concurrent access goes undetected, it can result in undefined behavior, application crashes and data corruption.


In addition to connecting with a read-only user, there are a few other things you can do to your 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>();
    }
}

As opposed to the accepted answer, I believe it would be better to favor composition over inheritance. Then there would be no need for keeping methods such as SaveChanges to throw an exception. Moreover, why do you need to have such methods in the first place? You should design a class in a way that its consumer doesn't get fooled when it looks at its list of methods. The public interface should be in align with the actual intent and goal of the class while in the accepted answer having SaveChanges doesn't imply that Context is read-only.

In places where I need to have a read-only context such as in the Read side of CQRS pattern, I use the following implementation. It doesn't provide anything other than Querying capabilities to its consumer.

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

By using ReadOnlyDataContext, you can have access to only querying capabilities of DbContext. Let's say you have an entity named Order, then you would use ReadOnlyDataContext instance in a way like below.

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

An alternate option, if you wanted to hand pick (and limit) which entities are exposed via this new context. You would remove the generic based method above (the complete block with TEntity in it) and use something similar to the below.

    public IQueryable<MyFirstThing> MyFirstHandPickThings => this.dbContext.Set<MyFirstThing>().AsNoTracking();

    public IQueryable<MySecondThing> MySecondHandPickThings => this.dbContext.Set<MySecondThing>().AsNoTracking();