Constructor injection for complex types
That is, bind an instance of a nested complex type to a constructor parameter.
Related to #12078
Vote
@el-genius please upvote (👍) the top-most comment rather than commenting.
Out of curiosity, would this be a reasonable ticket to pick up for a community contribution? Am I correct in assuming that this wouldn't really be touching query pipeline logic? Would be interesting in giving this a shot if not deemed complex enough to only be done by more seasoned EF contributors. Not had too deep of a look yet, but presumably most of the changes would be happening here? https://github.com/dotnet/efcore/blob/2ecd08f542d403d9ae006ab6fdb10f0efb73fe0b/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs#L251
/cc @AndriySvyryd. Also given we're doing a big push on complex types and given the number of votes, we may want to try to get this in for 10.
I'm not very familiar with this code, but you would also need to modify https://github.com/dotnet/efcore/blob/main/src/EFCore/Metadata/Internal/PropertyParameterBindingFactory.cs and https://github.com/dotnet/efcore/blob/main/src/EFCore/Query/Internal/EntityMaterializerSource.cs#L77
It would be significantly more complex to enable this for collections of complex types or complex properties mapped to JSON, so perhaps it's best to tackle those separately.
I have had a look at this issue and got a version working locally. The difficulty comes from creating the binding.
Regular properties are bound like this:
https://github.com/dotnet/efcore/blob/bd1e5327a789f2bf4eea251006f3e59467c573cc/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs#L137
Constructor parameter properties are bound like this:
https://github.com/dotnet/efcore/blob/bd1e5327a789f2bf4eea251006f3e59467c573cc/src/EFCore/Metadata/PropertyParameterBinding.cs#L34-L35
Complex properties are (currently) bound like this:
https://github.com/dotnet/efcore/blob/bd1e5327a789f2bf4eea251006f3e59467c573cc/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs#L145-L148
If you create a new parameter binding (ComplexPropertyParameterBinding), you need to do one of the following to get the binding expression:
- Copy the code from StructuralTypeMaterializerSource.cs
- Refactor the complex property binding logic from StructuralTypeMaterializerSource.cs into a separate service and inject into the
PropertyParameterBindingFactory - Pass a non-static func that calls
StructuralTypeMaterializerSource.CreateMaterializeExpressiondown through the constructor materialization logic
The last seems the easiest, the 2nd possibly more correct.
There would be a few more changes as well:
https://github.com/dotnet/efcore/blob/bd1e5327a789f2bf4eea251006f3e59467c573cc/src/EFCore/Metadata/Internal/PropertyParameterBindingFactory.cs#L44-L67
This would need an extra foreach through the complex properties in addition to the regular properties (easy to add a complexProperties param to this function though).
https://github.com/dotnet/efcore/blob/bd1e5327a789f2bf4eea251006f3e59467c573cc/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs#L269-L272
This needs an extra ?? entityType.FindComplexProperty(property.Name) in the chain.
I am happy to make a PR once the issue of how to bind the complex property is resolved, if there is an appetite for it.
It's probably worth noting that it is possible to fix this locally without editing the source (and potentially release the fix as a NuGet package). However, the required change in RuntimeModelConvention means that you not only need a custom RuntimeModelConvention, but also a custom RelationalRuntimeModelConvention and then a custom SqliteRuntimeModelConvention, SqlServerRuntimeModelConvention and so on).
@alasdair-cooper FYI we're heads down working on complex type support for JSON, so code in the area may change a bit. It's unfortunately unlikely we'll have time to complete this for EF 10 as the cutoff for feature work is quite soon - but I'll keep it on my radar just in case.