Entity Framework -- caching an object in memory across multiple contexts

c# entity-framework entity-framework-6 entity-framework-6.1 sql

Question

I am using Entity Framework 6 with a tenant-segregated application. I determine the tenant by looking at the request host name, then use it throughout the application, set it in tenant-owned records, et cetera.

Every context gets disposed at the end of the request. However, because the tenant lookups are so frequent, I actually only do them once per host name, then put the objects into a read-only dictionary in memory.

The problem here is that if you do nothing you end up with as many duplicate tenant records as you have requests (till the thing starts throwing because of now-ambiguous queries, anyway).

I initially resolved this by adding a call to DbSet.Attach() in my data store's constructor and attaching the current tenant. However, if you have multiple requests at the same time you get an exception informing you that you can't have the same object attached to multiple contexts: "An entity object cannot be referenced by multiple instances of IEntityChangeTracker." Since I occasionally trigger this on my dev machine by visiting pages too quickly I can't imagine it's suitable for production.

I tried changing things by instead adding this call before saving:

Context.ChangeTracker.Entries<Tenant>().Single().State = EntityState.Unchanged;

Well, that doesn't work either. I get the error "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects."

OK... so how do I do this? All I'm trying to do is (in terms of the final SQL result) end up with various rows that have a foreign key reference to an existing tenant row PK.

Some stuff I've found referencing EF4 has suggested the Detach method, but I'm not sure I'm meant to be calling that anymore since it's now hidden from the public interface of the DbSet. If I am I'm not sure where. When I first retrieve the record?

edit: One option that does seem to work is to go to the context and pull out the tenant record based on the cached record's ID. However, now I'm going and querying the database for no real reason.

1
4
4/30/2014 4:18:41 PM

Accepted Answer

It's not really satisfying, but ultimately the way I found to do this was to change my entities to include a property that was the foreign key:

[Required]
public Guid TenantId { get; set; }

[ForeignKey("TenantId")]
public virtual Tenant Tenant { get; set; }

Then I can freely set the TenantId field from the cache (since it's just a value type and not an EF-generated proxy class) and not worry about this problem.

0
12/5/2014 7:23:28 PM

Popular Answer

would it make more sense to cache a non-change-tracking, non-entity perspective of the data. It seems like the tenant record is a "pointer" to foreign key relationships.

logic to me is as follows:

  • do I have cached tenant info? nope.. go get it and cache it
  • now I need something related to the tenant
    • used the cached key relations to go do entity context queries
    • if you actually need the Tenant record, just .Include("tenant") on your query

for the most part, it seems like you would just need to persist the tenant id that is mapped to one or many host names. take the old-school ADO approach to this and strip the entity from your cache, just cache the POCO data you need.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow