zzzprojects / EntityFramework-Plus

Entity Framework Plus extends your DbContext with must-haves features: Include Filter, Auditing, Caching, Query Future, Batch Delete, Batch Update, and more
https://entityframework-plus.net/
MIT License
2.24k stars 318 forks source link

Using EF InMemory provider #86

Open mehmetilker opened 7 years ago

mehmetilker commented 7 years ago

When I run same repository code with EF Core InMemory provider I got follow exception:

Z.EntityFramework.Plus.QueryCache.EFCore.dll but was not handled in user code

Additional information: Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryContext' to type 'Microsoft.EntityFrameworkCore.Query.RelationalQueryContext'.

The reason I am using InMemory provider for integration test. I guess I shouldn't change my EF queries.

Is there a setting to overcome the issue or library should adapt the way how InMemory provider works ?

zzzprojects commented 7 years ago

Unfortunately, not all our feature support InMemory.

There is some way to currently overcome this limitation, but that will need you to use the Cache Key a little bit differently.

In short, you must either create your own cache key or force the first tag (must be unique for a specific query) to be used as a cache key.

CacheKeyFactory Allow to create yourself the key from the queriable and tags provided.

QueryCacheManager.CacheKeyFactory = (queryable, strings) => queryable.ToString() + string.Join(";", strings);

ForceFirstTagAsCacheKey Allow to use the first tag as cache key.

QueryCacheManager.ForceFirstTagAsCacheKey = true;
var list = ctx.Entity_Basics.Where(x => x.ColumnInt < value).FromCache("MyCacheKey", "tag1", "tag2");

I currently try do limit the support to this library to the minimum for a while since I must re-write all pro-library to support.NET Core/EF Core.

Let me know if this solution can work meanwhile.

Best Regards,

Jonathan

mehmetilker commented 7 years ago

For the second solution, I could not find QueryCacheManager.ForceFirstTagAsCacheKey but tried to set UseFirstTagAsCacheKey and it did not work.

First solution worked well. Although I could not understand the relation between casting exception and cache naming. At least there is already a cache name mechanism.

I set CacheKeyFactory just before .FromCache line... It is a global setting and should be set on startup or every Data context creation I guess ?

zzzprojects commented 7 years ago

When specifying yourself the cache key, we do not longer try to get the SQL Command from the IQueryable to create the cache key. So the code which throws the casting issue is not raised anymore.

We will let this issue open since we will try soon to fix this issue globally and improve cache key performance.

I highly recommend not using this quick fix for your production environment but only for your integration test until we fix it. The cache key is probably not unique in some situation when using this workaround.

mehmetilker commented 7 years ago

I see, I will try to find something else. Than you.

zzzprojects commented 7 years ago

We will make some fix on Cache Key this weekend. I will try to see if we can fix it as well.

zzzprojects commented 7 years ago

Hello @mehmetilker ,

After looking at this issue this weekend, we are still unsure how to handling it.

The only problem is making a unique cache key.

By example, for RelationalQueryContext, we simply concatenate the SQL generated with the parameter used. However, we cannot do the same for InMemory.

We will continue to investigate, but for now, we cannot provide support to InMemory.

Best Regards,

Jonathan

mehmetilker commented 7 years ago

Perhaps it can be InMemory provider problem... It should not work in a different way. Thank your for your time...

ksmithRenweb commented 7 years ago

Currently investigating this issue due to unit testing. My thought is that you are perhaps over-complicating it. 1) Batching of future transactions helps on round trips to database servers. Particularly important when that db server is not on the same machine. 2) In Memory is always local. (as far as i know) And its fast... It is also not relational.

So for InMemory, bypass batching. Short Circuit the issue. At a high level check for in memory and bypass all the batching logic. Just let the call happen normally for EFCore.

zzzprojects commented 7 years ago

Hello @ksmithRenweb ,

You are right, for the Query Future feature, we should simply skip the "batching part". I have added it to the current sprint, so it should be done within a few days.

You can follow the resolution of your solution here #102

I have created a new Issue since the one here is about the Query Cache feature.

Best Regards,

Jonathan

zzzprojects commented 7 years ago

Hello @mehmetilker ,

Using the solution of ksmithRenweb and making the fix simpler (at least, until we found a real solution), we will add an options to simply disabled the Query Cache feature.

So in short, when this options will be enabled for your unit testing, the cache will never be used but you will at least not have to change any code.

It's a solution which could satisfy your scenario? or you really need to have it work with InMemory also?

Best Regards,

Jonathan

mehmetilker commented 7 years ago

Hi @zzzprojects What I understood from @ksmithRenweb 's offering, skipping the caching if EF works with InMemory provider. So you will add an configuration option which I can set from my unit test project configuration ?

It doesn't sound right for integration testing but it will work as you said.

Thank you.

By the way, what I do simply is that while running integration test, configuring EF with InMemory provider istead of SqlServer and pre loading some data than running test methods.

fdbva commented 6 years ago

I have a similar problem and I don't think I understand the answer explained here. I couldn't make it work. I'm using .FromCache(typeof(class).Name) in some methods in my repository pattern. In the integration tests, I'm using: var optionsBuilder = new DbContextOptionsBuilder<context>(); optionsBuilder.UseInMemoryDatabase(dataBaseName) The exception I get is: 'Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryQueryContext' to type 'Microsoft.EntityFrameworkCore.Query.RelationalQueryContext'. at Z.EntityFramework.Plus.InternalExtensions.CreateCommand(IQueryable source, RelationalQueryContext& queryContext) at Z.EntityFramework.Plus.QueryCacheManager.GetCacheKey(IQueryable query, String[] tags) at Z.EntityFramework.Plus.QueryCacheExtensions.FromCache[T](IQueryable``1 query, MemoryCacheEntryOptions options, String[] tags)

I'm not sure where I should use: QueryCacheManager.UseFirstTagAsCacheKey = true;

Should I open a new Issue for this?

JonathanMagnan commented 6 years ago

Hello @fdbva ,

I recommend you to open a new issue and provide us all the code required to reproduce it (normally in a test project).

Best Regards,

Jonathan

fdbva commented 6 years ago

It might be a lot of code to reproduce, but I understand the need. Is there an way to disable FromCache that I could call inside my integration test method? If I can easily bypass FromCache on my test methods it would easily solve it.

JonathanMagnan commented 6 years ago

Answered in the issue #391