efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Constructor injection for complex types

Open ajcvickers opened this issue 2 years ago • 2 comments

That is, bind an instance of a nested complex type to a constructor parameter.

Related to #12078

ajcvickers avatar Sep 04 '23 07:09 ajcvickers

Vote

el-genius avatar Jun 02 '24 08:06 el-genius

@el-genius please upvote (👍) the top-most comment rather than commenting.

roji avatar Jun 02 '24 16:06 roji

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

bbartels avatar Apr 01 '25 11:04 bbartels

/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.

roji avatar Apr 01 '25 11:04 roji

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.

AndriySvyryd avatar Apr 01 '25 18:04 AndriySvyryd

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.CreateMaterializeExpression down 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 avatar Jul 26 '25 13:07 alasdair-cooper

@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.

roji avatar Jul 26 '25 17:07 roji