Instead of reading database lookup tables directly, team wants to apply (a) MemoryCache or (b) Entity Framework InMemoryDatabase/SQLite to reduce database usage and increase memory performance.
How to automatically forward existing Lookup Tables DbContext Linq queries to read from Memory? Lookup tables only change few times a year, are mostly static, AddressCategory, CustomerStatus, ProductType . The database is composed of Large Transaction tables which should be read from Database, and lookup tables to be read from Memory.
Note: Company has existing Linq queries, and want to auto forward existing code to Cache.
Method 1: EF InMemory / SQLite Proposed Solution:
Copy/Paste Original DbContext (StoreContext) into new DbContext copy called StoryMemoryContext. In Startup.cs
services.AddDbContext<StoreContext>(options => options.UseSqlServer(@"Server=localhost;Database=StoreDatabase";)); services.AddDbContext<StoreMemoryContext>(options => options.UseInMemoryDatabase(databaseName: "StoreMemoryContext")); services.AddDbContext<CustomDbContext>(options => options.UseSqlServer(@"Server=localhost;Database=StoreDatabase")));
Save Lookup Mini lookup tables into InMemoryDatabase 'StoryMemoryContext' (around 5 Mb max).
- Have CustomDbContext inherit from original, where Original StoreContext get accessors forward to StoryMemoryContext.
Loop code all lookup Tables with T4 or Powershell automation
public class CustomDBContext : StoreContext
{
public StoreMemoryContext _storeMemoryContext; = new StoreMemoryContext();
public CustomDBContext()
{
}
public override DbSet<ProductType> ProductType
{
set
{
base.ProductType = value;
_storeMemoryContext.ProductType = value;
}
get
{
return _storeMemoryContext.ProductType;
}
}
Questions:
1) Would this Get method work? Any issues to research for? Feel free to revise/edit in answer. Open to any solution not only this,
Get accessors seems to be working.
If executing
_customDbContext.ProductType.ToList()
, it reads InMemory EF._customDbContext.CustomerTransaction.Include(c => c.ProductType).ToListAsync()
will read database, matching intended behavior. Don't want to read large non-lookup table customer transaction from InMemory Database.
2) Set Accessors only partially works, since many ways exist to modify DBSet and DBContext: Add, AddRange, Remove, Add Entity Graph (is challenging to track) Physical lookup tables change only couple times a year. Thought about ChangeTracker, however to place If statement after every SaveChanges()
to validate if lookup table changed/then update InMemory database, may slow application, as we have 500 transactions/per second. (this is what I was told? willing to hear more opinions about ChangeTracker and effect on performance/speed)
Note: Physical and InMemory Lookup tables, can have 30 min sync period between two, per customer requirement, as lookup tables rarely change. However near instantaneous data sync between two is goal solution for question(< 5 sec).
Method 2: MemoryCache Does not Seem to Work: Similar create Inherited/custom DbContext where read overrides forward lookup tables to MemoryCache. Storing DBContext in MemoryCache only stores IQueryable, not actual Materialized values. Going from controller to controller refreshes DbContext, and clears out cache, feel free to revise/edit in answer.
public class CustomDBContext : EcommerceContext
{
public CustomDBContext(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public override DbSet<ProductType> ProductType
{
get
{
return _memoryCache.Get<DbSet<ProductType>>("ProductTypeMemoryCache");
}
}
Update:
Answer from David Browne will not work for question requirements, as has Usage like
var pt2 = db.Cache.LK_ProductTypes; //from cache
Company has many existing queries, and want to forward existing code to Cache. Additionally, programmers may forget to apply Cache, so code below should automatically forward
var pt1 = db.LK_ProductTypes.ToList() ;
We have mix of large transactional tables which should not be cached and small lookup tables.
DBA does not allow use SQL Server memory-optimized tables due to some limitations. Memory Optimized restrictions
Resources:
Does DbContext need MemoryCache or Redis in Net Core?
How to make Entity Framework Data Context Readonly
Net Core: Automatically Update Lookup Table Cache after Entity Framework Save and UnitOfWorkPattern
Using EF Core 2.2,