DbContexts should be short-lived. If you want instances that are not tracked/cached then you can use AsNoTracking()
in the query, however this would need to be done consistently as I believe an AsNoTracking()
query could still return a previously tracked entity. (I.e. from another call against that DbContext instance that didn't use AsNoTracking()
To ensure a DbContext always returns the current state from the database consistently, one approach would be to hide the DbSets and expose IQueryable<TEntity>
alternatives.
For instance, instead of:
public class AppDbContext : DbContext
{
public DbSet<Orders> Orders { get; set; }
public DbSet<Customers> Customers { get; set; }
}
public class AppReadDbContext : DbContext
{
public DbSet<Order> _Orders { protected get; set; }
public DbSet<Customer> _Customers { protected get; set; }
public IQueryable<Order> Orders => _Orders.AsNoTracking();
PUBLIC IQueryable<Customer> Customers => _Customer.AsNoTracking();
}
Caveat: That's an idea I haven't confirmed works or not, will try it out tomorrow morning.
Update: Tested it this morning and applied a few changes. Protected DbSets don't populate so left the DbSet public with a protected Getter. This is to discourage code attempting to use the DbSet rather than the AsNoTracking()
IQueryable
. Verified the behaviour with the following test:
[Test]
public void TestNoTracking()
{
using (var context = new AppReadDbContext()) // protected DbSet getter with NoTracking IQueryable getter.
{
var customer = context.Customers.Single(x => x.CustomerId == 2);
Assert.AreNotEqual("Alex", customer.Name);
using (var context2 = new AppDbContext()) // normal DbSets
{
var c = context2.Customers.Single(x => x.CustomerId == 2);
c.Name = "Alex";
context2.SaveChanges();
}
Assert.AreNotEqual("Alex", customer.Name);
customer = context.Customers.Single(x => x.CustomerId == 2);
Assert.AreEqual("Alex", customer.Name); // was fetched from DB.
}
}
Note that this would be only really suitable for read operations. You can do updates without tracking but it is less efficient. Generally for most operations aim to use short-lived DbContexts, and ensure they are disposed afterwards. (I.e. using()
block)