Conversation
| /// any release. You should only use it directly in your code with extreme caution and knowing that | ||
| /// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
| /// </summary> | ||
| public async Task GeneratePrecompiledQueries( |
There was a problem hiding this comment.
Mainly for @AndriySvyryd: this method is currently just hacked up to be invoked from e.g. an external console program, and needs to be adapted (in case it's not obvious). The precompiled query tests I've been working with call the method below, which just accepts a Compilation directly.
| out var createCollection); | ||
|
|
||
| // TODO | ||
| var unsafeAccessors = new HashSet<string>(); |
There was a problem hiding this comment.
@AndriySvyryd note the unsafe accessors now getting generated inside LinqToCSharpSyntaxTranslators - ideally these would be used here instead of the current method replacements mechanism?
There was a problem hiding this comment.
There are two reasons why I implemented them differently:
- Avoid generating duplicate accessors for the same members
- Support private property access
There was a problem hiding this comment.
For the deduplication, this already occurs within the translator itself, so we could expose what's needed to allow deduplication across multiple invocations of the translator. For example, we could have the same instance of the translator accumulate unsafe accessor declarations internally as its reused, and at the end you'd all of them via a special API. Or we could externalize that state so you'd pass it back in, etc.
(For that state, originally I had a nice and clean dictionary from MemberInfo to the MethodDeclarationSyntax representing its interceptor - but needing different interceptors for field read/write makes that a tiny bit more complex.)
Private property access should also already be implemented in the translator - let me know if you see an issue.
There was a problem hiding this comment.
Using the same instance won't help when processing an assembly that doesn't contain the compiled model.
I was thinking that we can store memberAccessReplacements as an annotation in the compiled model and precompiled queries would use it. If you think that it's possible to have queries that access a private member that's not in the model then we can also keep unsafeAccessors
There was a problem hiding this comment.
So FWIW, I looked again and the current implementation already deduplicates unsafe accessors across invocations (i.e. across shapers of different queries) - the state passed around is simply ISet<MethodDeclarationSyntax>, and at the end we dump that out into the generated file.
Using the same instance won't help when processing an assembly that doesn't contain the compiled model.
Let's do a call so I can understand this better (possibly tomorrow before/after the design meeting?)
There was a problem hiding this comment.
@AndriySvyryd for now I've left the these changes as they are in the PR, let's revisit and clean up after merge OK?
src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
Outdated
Show resolved
Hide resolved
d68d249 to
6b20e62
Compare
165f114 to
53025c3
Compare
There was a problem hiding this comment.
I've more of less finished what I consider to be the most of the work here. There are some edge cases I know which aren't handled (and most probably some others I don't know), but I think it's in good enough shape to merge and fix local problems later.
So moving this out of draft state - it should be ready for a good review. Note that this is still based on top of #33351 which isn't yet done; be sure to exclude that when reviewing.
| selectExpression.Orderings, | ||
| selectExpression.Limit, | ||
| selectExpression.Offset)); | ||
| selectExpression.Offset, |
There was a problem hiding this comment.
Note that here - and below - I'm generally moving arround SelectExpression components to be in the order in which they're evaluated in SQL (i.e. offset before limit). We weren't always consistent, and the confusion can be hard to track down.
| out var createCollection); | ||
|
|
||
| // TODO | ||
| var unsafeAccessors = new HashSet<string>(); |
There was a problem hiding this comment.
@AndriySvyryd for now I've left the these changes as they are in the PR, let's revisit and clean up after merge OK?
For your convenience: https://github.com/dotnet/efcore/pull/33297/files/b66d7b9c4fd5846ff6e6afa8e71dfe2f97983aa5..53025c3bfb3503257a49f2d96327e4c9ee1485db |
185635c to
c20e1b4
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisVersion)" /> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0-2.final" /> |
There was a problem hiding this comment.
This should be probably fixed back before merging.
There was a problem hiding this comment.
This version contains an interceptor-related compiler fix which is required for the tests to run successfully - so I don't think we'll be able to change this back. However, it's a test-only dependency on a preview package, and 4.10.0 should release for 9.0, at which point hopefully we can align everything.
There was a problem hiding this comment.
I mean we can change the MicrosoftCodeAnalysisVersion variable. For 9.0 RTM everything will align fine.
There was a problem hiding this comment.
We can, but this would limit the SDK versions that can be used by ordinary users with EF... We've had issues with changes like this in the past - given that we only need this in testing, I'd rather we left things as-is for now and update later as needed...
test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj
Show resolved
Hide resolved
...es/Custom_function_parameter_type_mapping/FunctionParameterTypeMappingContextModelBuilder.cs
Show resolved
Hide resolved
f9095d8 to
a0349fd
Compare
| // containing type (and recursively, its containing types) | ||
| var typeTypeParameterMap = new Dictionary<string, Type>(GetTypeTypeParameters(methodSymbol.ContainingType)); | ||
|
|
||
| IEnumerable<KeyValuePair<string, Type>> GetTypeTypeParameters(INamedTypeSymbol typeSymbol) |
There was a problem hiding this comment.
Move to the end and make static
There was a problem hiding this comment.
I don't think this can be static, since it invokes ResolveType... But moving to the end.
| ? ResolveType(typeSymbol) | ||
| : throw new InvalidOperationException("Could not find type symbol for: " + node); | ||
|
|
||
| private Type ResolveType(ITypeSymbol typeSymbol, Dictionary<string, Type>? genericParameterMap = null) |
There was a problem hiding this comment.
Review whether more calls of this should be done while supplying a genericParameterMap. For example, the expression could be referencing a generic type parameter from the containing type.
There was a problem hiding this comment.
Yeah, I suspect there are a few bugs lurking around this... With everything going on, I'll defer this to now, and have added a note in #33639. We can fix later based on feedback or do a more thorough review.
|
|
||
| // If the member isn't declared on the same type as the expression, (e.g. explicit interface implementation), add | ||
| // a cast up to the declaring type. | ||
| _ when member.Member.DeclaringType is Type declaringType && declaringType != member.Expression.Type |
There was a problem hiding this comment.
Add a check whether the declaring type is an interface or the member is hidden
There was a problem hiding this comment.
Changed to only add the parentheses for interface-declared members - but what do you have in mind for hidden?
There was a problem hiding this comment.
It's a corner case, but if the current type hides the member being used then we'd need to add a cast to the declaring type to be sure that the same member is used
src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs
Outdated
Show resolved
Hide resolved
| namespace System.Runtime.CompilerServices | ||
| { | ||
| [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] | ||
| sealed class InterceptsLocationAttribute : Attribute |
There was a problem hiding this comment.
| sealed class InterceptsLocationAttribute : Attribute | |
| internal sealed class InterceptsLocationAttribute : Attribute |
There was a problem hiding this comment.
I think it should actually be file-scoped...
|
|
||
| #nullable enable | ||
|
|
||
| public class CSharpToLinqTranslatorTest |
There was a problem hiding this comment.
Consider merging with LinqToCSharpSyntaxTranslatorTest as roundtrip tests
There was a problem hiding this comment.
IMHO it leaves things simpler for each test/test class to focus on one component here, given the complexity of the components...
Note also that the scope of each component here is very different - CSharpToLinq only needs to handle expression types supported by the compiler (i.e. only expression stuff, no loops/blocks/try catch), whereas LinqToCSharp needs to in theory handle everything (or at least everything we actually use in our shapers).
| => AssertExpression( | ||
| () => (object)new Blog(), | ||
| "(object)new CSharpToLinqTranslatorTest.Blog()"); | ||
|
|
There was a problem hiding this comment.
Add tests for anonymous type, different closures, nested generics, indexers, records with
There was a problem hiding this comment.
Added note in #33639. I hope we get to do a good bash on this before the release, and flush out bugs.
a0349fd to
4eaafe5
Compare
4eaafe5 to
8f4ef6d
Compare
The precompiled query work is in good enough shape that I think it's the right moment to submit as a draft PR. There are still various test failures, checks and cleanups which I plan to implement, but it's more about edge cases at this point - the overall architecture should be sound. I'm happy to do a session with whoever wants to dive in and at least explain the general approach (we can also do a brown bag at some point if people want to).
Notes:
Closes #31331