Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QueryCache misses for dynamically created queries with ConstantExpresssions or nested MemberExpressions from closure #8909

Closed
Tasteful opened this issue Jun 20, 2017 · 30 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@Tasteful
Copy link
Contributor

I got a lot of dupplicated entries in the MemoryCache that origin from EF and many of them contains the same sql query and is generated from the exact same code.

Example the following that generate an SQL query that is duplicated in cache more than 16 000 times.

image

When I checking the entity in the MemoryCache we can se a lot of repeated entries that is 14 338 bytes each but contains the same query.

image

The only "special" that I can remember that I have done is that the query is built with System.Linq.Expressions.Expression that is added to the query with .Where(expression).

What is the best approach to find the reason for this cache misses?

Further technical details

EF Core version: 1.1.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017

@Tasteful
Copy link
Contributor Author

Tasteful commented Jun 20, 2017

It seems that is the usage of System.Linq.Expressions.Expression that is bad in my usage case.

  • Is there any reason to not use the string representation as the cache key?
  • I can't find a way to change the sliding timeout for the cache entry per query, is that possible?

@Tasteful
Copy link
Contributor Author

Is it the same when using ICollection<T>.Contains?

example

var values = new List<Guid>();
values.Add(Guid.NewGuid());
values.Add(Guid.NewGuid());
values.Add(Guid.NewGuid());
dbContext.Table1.Where(x => values.Contains(x.Id)).ToList();

For the contains it generate an SQL with the values in the statement, not as parameters. Will the cache handle that to replace the parameters?

@anpete
Copy link
Contributor

anpete commented Jun 20, 2017

@Tasteful Sorry you are hitting this. Can you post the complete LINQ query that causes the cache miss?

@Tasteful
Copy link
Contributor Author

@anpete I will try to make a breakout of relevant code parts.

@smitpatel
Copy link
Member

EF Core version: 1.1.1

Any reason you haven't upgraded to 1.1.2?
You filed issue in past about memory leak which was fixed in 1.1.2 #8063 😄

@anpete
Copy link
Contributor

anpete commented Jun 20, 2017

Oh yeah, definitely try that 😄

@Tasteful
Copy link
Contributor Author

Was it the one that i found with Db.Set, then I already have that fix localy. Was it any other fixes in 1.1.2 regarding to query-cache?

It was found in production system and want to be able to produce same issue localy on the same version before upgrade and trying any fixes.

@anpete
Copy link
Contributor

anpete commented Jun 20, 2017

@Tasteful #7899 is the only other one I can see, but I suspect you are not hitting that.

@Tasteful
Copy link
Contributor Author

@anpete Tested this with both the 1.1.1 and 1.1.2 release and both version have the same behavour

I have put a test-repo here https://github.com/Tasteful/bugs/blob/query-cache2 and if you check the https://github.com/Tasteful/bugs/blob/query-cache2/MemoryUsage/Program.cs and specific the method TestContains that is working, will produce the following expression tree

.Call System.Linq.Queryable.Where(
    .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MemoryUsage.MyTable1]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MemoryUsage.MyTable1]),
    '(.Lambda #Lambda1<System.Func`2[MemoryUsage.MyTable1,System.Boolean]>))

.Lambda #Lambda1<System.Func`2[MemoryUsage.MyTable1,System.Boolean]>(MemoryUsage.MyTable1 $item) {
    .Call (.Constant<MemoryUsage.Program+<>c__DisplayClass0_0>(MemoryUsage.Program+<>c__DisplayClass0_0).itemIds).Contains($item.Id)
}

and compare that to TestContainsFromExpression-method that create the same expression tree but is not working.

.Call System.Linq.Queryable.Where(
    .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MemoryUsage.MyTable1]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MemoryUsage.MyTable1]),
    '(.Lambda #Lambda1<System.Func`2[MemoryUsage.MyTable1,System.Boolean]>))

.Lambda #Lambda1<System.Func`2[MemoryUsage.MyTable1,System.Boolean]>(MemoryUsage.MyTable1 $item) {
    .Call (.Constant<MemoryUsage.Program+<>c__DisplayClass0_4>(MemoryUsage.Program+<>c__DisplayClass0_4).itemIds).Contains($item.Id)
}

If I not can add and use the Contains method dynamic I will get a limit on the max parameter count that sql server can handle for the items in the list.

Then we have method TextExpressionFunc that is working and TestExpressionConst that not is working,.

For all versions that not is working I found that we use the Expression.Constant and that is probably why I got the repeated query in cache of size 14 338 bytes.

I will continue with the larger SQL that was duplicated 16 000 times and see if I can make a simple replication there also.

@Tasteful
Copy link
Contributor Author

If I remember correct the QueryCache is caching all the queries infinity, is that a big benefit to do that instead of have a sliding cache timeout to clear out queries that not is used frequently?

@anpete
Copy link
Contributor

anpete commented Jun 20, 2017

The first thing is that there should only be one entry for each query, so we will look at that (thanks for the repro).

We are using the ASP.NET MemoryCache, which does have some configuration options.

@Tasteful
Copy link
Contributor Author

What I can see in https://github.com/aspnet/EntityFramework/blob/dev/src/EFCore/Query/Internal/CompiledQueryCache.cs is that no options are set when the item is adding to the cache. The old v1 implementation of that is removing 10% of cache entries when gc gen 2 is happening. This will be changed in v2 aspnet/Announcements#228 and if no expiration time is set the cache entries will probably live forever.

@anpete
Copy link
Contributor

anpete commented Jun 20, 2017

@Tasteful I guess we could set a value for SlidingExpiration as a mitigation for these kinds of bugs. I think you could also manually call the Compact method as a workaround.

@Tasteful
Copy link
Contributor Author

Tasteful commented Jun 21, 2017

For the one where the same SQL is stored multiple times the callstack is the following and result down into a stringbuilder. The string builder internal use an char arrayto keep track of the data. I'm not sure if this string builder is populated with data for each query or that the StringBuilder can be replaced with a string after it is populated to not keep track of all the chars.

image

The expression for that looks like

.Call System.Linq.Queryable.Where(
    .Call System.Linq.Queryable.Where(
        .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MemoryTest.Application.Products.Data.VariantEntity]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MemoryTest.Application.Products.Data.VariantEntity]),
        '(.Lambda #Lambda1<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity,System.Boolean]>)),
    '(.Lambda #Lambda2<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity,System.Boolean]>))

.Lambda #Lambda1<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity,System.Boolean]>(MemoryTest.Application.Products.Data.VariantEntity $item)
{
    .Call System.Linq.Queryable.Count(
        .Call System.Linq.Queryable.Where(
            .Call ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).dbContext).Set()
            ,
            '(.Lambda #Lambda3<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity+FieldDataEntity,System.Boolean]>)),
        (.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).exp)
    > 0 || .Call System.Linq.Queryable.Count(
        .Call System.Linq.Queryable.Where(
            .Call ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).dbContext).Set()
            ,
            '(.Lambda #Lambda4<System.Func`2[MemoryTest.Application.Products.Data.BaseProductEntity+FieldDataEntity,System.Boolean]>))
        ,
        .Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).baseProductExp)
    > 0
}

.Lambda #Lambda2<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity,System.Boolean]>(MemoryTest.Application.Products.Data.VariantEntity $item)
{
    .Call System.Linq.Queryable.Count(
        .Call System.Linq.Queryable.Where(
            .Call ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).dbContext).Set()
            ,
            '(.Lambda #Lambda5<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity+FieldDataEntity,System.Boolean]>)),
        (.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).exp)
    > 0 || .Call System.Linq.Queryable.Count(
        .Call System.Linq.Queryable.Where(
            .Call ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).dbContext).Set()
            ,
            '(.Lambda #Lambda6<System.Func`2[MemoryTest.Application.Products.Data.BaseProductEntity+FieldDataEntity,System.Boolean]>))
        ,
        .Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).baseProductExp)
    > 0
}

.Lambda #Lambda3<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity+FieldDataEntity,System.Boolean]>(MemoryTest.Application.Products.Data.VariantEntity+FieldDataEntity $x)
{
    $x.OwnerId == $item.SystemId && $x.Id == ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).f).Id
    && $x.Culture == (.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).cultureName
}

.Lambda #Lambda4<System.Func`2[MemoryTest.Application.Products.Data.BaseProductEntity+FieldDataEntity,System.Boolean]>(MemoryTest.Application.Products.Data.BaseProductEntity+FieldDataEntity $x)
{
    ($x.Owner).SystemId == $item.BaseProductSystemId && $x.Id == ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).f).Id
    && $x.Culture == (.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).cultureName
}

.Lambda #Lambda5<System.Func`2[MemoryTest.Application.Products.Data.VariantEntity+FieldDataEntity,System.Boolean]>(MemoryTest.Application.Products.Data.VariantEntity+FieldDataEntity $x)
{
    $x.OwnerId == $item.SystemId && $x.Id == ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).f).Id
    && $x.Culture == (.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).cultureName
}

.Lambda #Lambda6<System.Func`2[MemoryTest.Application.Products.Data.BaseProductEntity+FieldDataEntity,System.Boolean]>(MemoryTest.Application.Products.Data.BaseProductEntity+FieldDataEntity $x)
{
    ($x.Owner).SystemId == $item.BaseProductSystemId && $x.Id == ((.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).f).Id
    && $x.Culture == (.Constant<MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1>(MemoryTest.Application.Products.Data.Querying.VariantFieldQueryExpressionInfoBuilder+<>c__DisplayClass3_1).CS$<>8__locals1).cultureName
}

and I can see that .Constant is used frequently in that expression also.

The inner part

.Lambda #Lambda1<System.Func`2[MemoryTest.FieldFramework.IFieldData,System.Boolean]>(MemoryTest.FieldFramework.IFieldData $x) {
    $x.TextValue == .Constant<MemoryTest.Application.FieldFramework.FieldTypes.TextFieldMetadata+TextField+<>c__DisplayClass2_0>(MemoryTest.Application.FieldFramework.FieldTypes.TextFieldMetadata+TextField+<>c__DisplayClass2_0).v .As
    System.String
}

is created from an method as below and added to the query with .Where(GetExpression("my value")).

private Expression<Func<IFieldData, bool>> GetExpression(object v)
{
    return x => x.TextValue == v as string;
}

@anpete

@anpete
Copy link
Contributor

anpete commented Jun 23, 2017

@Tasteful I have made some progress on this. It is indeed to do with constant nodes in the expression tree. Normally when we see a constant node in the tree, it is because the value was in-lined in the query directly. E.g. a c# literal. Hence, we make the decision to not parameterize it because it is essentially hard-coded in the query. On the other hand, values coming from variables are captured by the compiler into a closure, and show up in the expression tree as member access expressions. These we parameterize. So, to start with, I think a workaround for this case is to introduce member access nodes in your dynamic LINQ expression instead of constants (by assigning the list of values to a field on a holder object and then creating a member expression to the field).

@Tasteful
Copy link
Contributor Author

@anpete Thanks for the proposed workaround, I will try to rewrite the code to user member access expressions instead.

This is "little tricky" to find as an developer that only is using the library. Are you consider to rewrite the internal part to also parameterize the constant expression node or does it exists any other smart thing to make better decission for real constants vs. local variables?

@anpete
Copy link
Contributor

anpete commented Jun 26, 2017

Things we can do:

  1. Allow SlidingExpiration to be set via Options.
  2. Keep track of count of queries in cache and either warn, or call Compact, or both.
  3. Add an Option that controls whether or not constants get parameterized.
  4. Support ParameterExpressions in the parameterizer as a way to more easily get caching when building dynamic queries.

Punting for now.

@Tasteful
Copy link
Contributor Author

@anpete Before adding call to Compact method from EF, consider this aspnet/Announcements#228 where the auto call during Gen2 GC happend was removed due to issues in other areas.

@BradBarnich
Copy link

@anpete Thanks. I was running into this issue as well, using constants when building expressions from our internal query objects. Changing to member access made the process memory usage flat and improved perf quite a bit too.

@anpete
Copy link
Contributor

anpete commented Jun 27, 2017

Great to hear the workaround is somewhat acceptable. 😄

@Tasteful
Copy link
Contributor Author

@anpete I have some problems to get it to work. Not sure what I'm doing wrong, maybe you can point me in correct direction. I put a short example here https://github.com/Tasteful/bugs/blob/query-cache-example/MemoryUsage/Program.cs with 3 different methods to create the expression and only the one with Constant (method CreateExpression) is returning that expression is the same, but the expression is only the same for the same input.

The hash and comparison is done with the Microsoft.EntityFrameworkCore.Query.Internal.ExpressionEqualityComparer that is used for calculating of the query cache key.

Output from program, original code was like the one in method CreateExpression3

Example CreateExpression
hash1: -1699865404
hash2: -1699865404
eq: True
Example CreateExpression2
hash1: -756323298
hash2: 321098734
eq: False
Example CreateExpression3
hash1: -561453146
hash2: 1704667454
eq: False

@anpete
Copy link
Contributor

anpete commented Jun 27, 2017

@Tasteful Are you running parameter extraction over the ET first? I.e. Use ParameterExtractingExpressionVisitor to turn the member expressions into parameters.

@Tasteful
Copy link
Contributor Author

@anpete No, I didn't. I was extracting the expressions that I run inside the DbContext-object and was thinking that the object was "ready to calculate hash on" I will rewrite and execute them in the DbContext to get the ParameterExtractingExpressionVisitor to run. I will come back with result.

@Tasteful
Copy link
Contributor Author

@anpete When I run the code as a query in the DbContext it was working. Then I need to rewrite the unit test to also use the ParameterExtractingExpressionVisitor to test the specific methods that is creating the method. Thanks for the help.

@Tasteful
Copy link
Contributor Author

@anpete I still have problem with rewriting the expression into usage of MemberExpressions to access the data. Should the below expressions work? (full repo: https://github.com/Tasteful/bugs/blob/query-cache-expression-not-working)

Code that is working but I'm not able to use due that I need to construct the expressions dynamic, (headline in output Query cache is working, using the dbContext.MyTable2)

var id = Guid.NewGuid().ToString("N");
var items = dbContext.MyTable1
    .Where(item => dbContext.MyTable2
        .Where(x => x.Id == item.Id)
        .Where(x => x.Name == id)
        .Any())
    .ToList();

Manually creating lambda expression that is used where-expression. For me this should be equalent to the above code. (headline in output Query cache is not working, using the dbContext.Set<MyTable2>)().Where(LambdaExpression)

var id = Guid.NewGuid().ToString("N");
Expression<Func<MyTable2, bool>> whereExpression = item => item.Name == id;
var items = dbContext.Set<MyTable1>()
    .Where(item => dbContext.Set<MyTable2>()
        .Where(x => x.Id == item.Id)
        .Where(whereExpression)
        .Any())
    .ToList();

Manually creating expression that is used where-expression. (headline in output Query cache is not working, using the dbContext.Set<MyTable2>)().Where(manual created expression)

var id = Guid.NewGuid().ToString("N");
Expression<Func<string>> valueExpression = () => id;
var param = Expression.Parameter(typeof(MyTable2), "item");
var property = Expression.Property(param, nameof(MyTable2.Name));
Expression result = Expression.Equal(property, Expression.Invoke(valueExpression));
var whereExpression = Expression.Lambda<Func<MyTable2, bool>>(result, param);
var items = dbContext.Set<MyTable1>()
    .Where(item => dbContext.Set<MyTable2>()
        .Where(x => x.Id == item.Id)
        .Where(whereExpression)
        .Any())
    .ToList();

Program output

Query cache is working, using the dbContext.MyTable2
Before iteration 0 query cache count 0
After iteration 0 query cache count 1
Before iteration 1 query cache count 1
After iteration 1 query cache count 1
Before iteration 2 query cache count 1
After iteration 2 query cache count 1
Before iteration 3 query cache count 1
After iteration 3 query cache count 1
Before iteration 4 query cache count 1
After iteration 4 query cache count 1

Query cache is not working, using the dbContext.Set<MyTable2>().Where(LambdaExpression)
Before iteration 0 query cache count 1
After iteration 0 query cache count 2
Before iteration 1 query cache count 2
After iteration 1 query cache count 3
Before iteration 2 query cache count 3
After iteration 2 query cache count 4
Before iteration 3 query cache count 4
After iteration 3 query cache count 5
Before iteration 4 query cache count 5
After iteration 4 query cache count 6

Query cache is not working, using the dbContext.Set<MyTable2>().Where(manual created expression)
Before iteration 0 query cache count 6
After iteration 0 query cache count 7
Before iteration 1 query cache count 7
After iteration 1 query cache count 8
Before iteration 2 query cache count 8
After iteration 2 query cache count 9
Before iteration 3 query cache count 9
After iteration 3 query cache count 10
Before iteration 4 query cache count 10
After iteration 4 query cache count 11

@Tasteful
Copy link
Contributor Author

Tasteful commented Jul 2, 2017

It seems that this is a regression bug from my earlier PR #4431

To generate correct hashcode for multiple queries the change below need to be changes (PR is on the way)

https://github.com/aspnet/EntityFramework/blob/dev/src/EFCore/Query/ExpressionVisitors/Internal/ParameterExtractingExpressionVisitor.cs#L355-L358

if (parameterValue is Expression parameterExpression)
{
    return ExtractParameters(parameterExpression);
}

And for the older 1.1.2 release
https://github.com/aspnet/EntityFramework/blob/rel/1.1.2/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/ParameterExtractingExpressionVisitor.cs#L318-L321) should be like the following

            if (parameterExpression != null)
            {
                return ExtractParameters(parameterExpression);
            }

@anpete

@divega
Copy link
Contributor

divega commented Jul 3, 2017

From what I am hearing there seem to be actions to take in 2.0. Clearing up milestone so that this doesn’t get lost in the backlog.

@ajcvickers
Copy link
Member

@smitpatel What's the status on this one? Do we know what needs to happen to trigger a memory leak?

@smitpatel
Copy link
Member

There are 2 issues here,
The first issue as outlined by @anpete here #8909 (comment)
When we see ConstantExpression in ExpressionTree we don't make them parameter but they generally come from inlined values. So if you are generating queries dynamically creating ConstantExpression, it will be different cache entry each time.

For the second issue,
We parametrize variables coming from closure. Which are put in as MemberExpression by compiler. We detect such MemberExpression and convert them to parameter but that logic was single level only. i.e. If closure.member is a value then only we make a parameter otherwise we don't. For the cases when some expression is being passed from closure, we unwrap the expression from closure.member but we don't process the expression to find more closure variables. Hence whatever is left in the ExpressionTree from closure is converted to ConstantExpression by ReLinq query parser. So we miss out on opportunity to parametrize. The fix is to apply ParameterExtraction recursively if we get expression back from closure.

@smitpatel smitpatel added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jul 7, 2017
@smitpatel
Copy link
Member

Filed #9105 for patch consideration.

@smitpatel smitpatel changed the title QueryCache misses for the same query make huge memory usage QueryCache misses for dynamically created queries with ConstantExpresssions or nested MemberExpressions from closure Jul 7, 2017
maumar added a commit that referenced this issue Aug 26, 2017
… ConstantExpresssions or nested MemberExpressions from closure

Adding parametrization for nested lambda expressions
maumar added a commit that referenced this issue Aug 26, 2017
… ConstantExpresssions or nested MemberExpressions from closure

Adding parametrization for nested lambda expressions
artyomszasa added a commit to artyomszasa/NCoreUtils.Data that referenced this issue Jul 13, 2018
instead of constant member access expression must be generated to avoid cache issues.
see: dotnet/efcore#8909
see: dotnet/efcore#10535
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

6 participants