dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.76k stars 3.18k forks source link

Complex type collection support #31237

Open AndriySvyryd opened 1 year ago

AndriySvyryd commented 1 year ago

The collections need to be ordered, since they will be mapped to JSON arrays. (Unless we allow to map to objects, see https://github.com/dotnet/efcore/issues/28861)

This only covers collections of reference types. For value types see #31411

roji commented 10 months ago

Note also #11799 which is about supporting tuples as well.

rlcarrierscsharp commented 6 months ago

I don't suppose I could get lucky enough to have someone from the EF Core team at a high level explain what changes would need to be made to get this to work? I really have a need for this now and it would be great to make the changes on my own while I wait for this to be officially completed. Or at least get the change tracking working, even if the database doesn't actually get updated would be fine.

I can see that the ComplexProperty already has the IsCollection property on it. Would changes just need to be made to the SnapshotFactoryFactory, ValueComparer, and NavigationFixer classes - again, without concern of generating the proper database statements to sync the data model with the database - in order to have complex collections have change tracking support?

roji commented 6 months ago

This is a pretty deep change requiring adjustments in many different components of EF (modeling, change tracking, querying, updating...), it's unlikely you'll be able to correctly and easily do these changes on your own as a quick project etc.

Remember that you can currently already use owned entities as collections in order to map JSON. This is the recommend thing until complex type collection support is built into EF.

rlcarrierscsharp commented 6 months ago

This is a pretty deep change requiring adjustments in many different components of EF (modeling, change tracking, querying, updating...), it's unlikely you'll be able to correctly and easily do these changes on your own as a quick project etc.

Remember that you can currently already use owned entities as collections in order to map JSON. This is the recommend thing until complex type collection support is built into EF.

I appreciate the quick reply! I actually don't need to map to JSON at all. I just need navigation and tracking. I've been studying the code from EF7 quite a bit and just recently switched to EF 8 in the hopes of using complex types with collection support (found out after the switch collections are not yet supported).

It might be over my head. I was hoping with a high level overview I'd be able to do it, but I understand if you don't have the time to write all of it out. I figured it wouldn't be too bad since EF Core already supports complex types with nested / navigation to other entity types. I'm familiar with how entity types are defined; how properties / navigations are built up using the internal builders, which get created into a RuntimeModel through the RuntimeModelConvention class on DbContext creation; how snapshots are created, how there is a relationship snapshot, original values snapshot, how those values are "copied" and set in one of the Snapshot classes; how these snapshot values are looked up later via the property's index of interest (relationship index, original value index, etc.). Basically, I've been studying the code for a bit now and while I am no expert by any means, I am not a stranger either.

I will admit Owned Types are one of the things I have not explored as of yet. I basically want to be able to pass in an IIS data model, which as you can imagine, doesn't quite fit very well into how EF Core handles entities. I'm not sure if Owned Types would be the solution here, but it seems like Complex Types would do the trick (if collections were supported).

public class ApplicationPool
{
    public string Name { get; set; } = null!;

    public bool AutoStart { get; set; }

    public bool Enable32BitAppOnWin64 { get; set; }

    public string RuntimeVersion { get; set; }

    public List<Site>? Sites { get; set; }

    public List<Application>? Applications { get; set; }
}

public class VirtualDirectoryModel
{
    public string LogonMethod { get; set; } = null!;

    public string Path { get; set; } = null!;

    public string PhysicalPath { get; set; } = null!;

    public Application Application { get; set; } = null!;
}
public class Application
{
    public string ApplicationPoolName { get; set; } = null!;

    public string Path { get; set; } = null!;

    public List<VirtualDirectoryModel> VirtualDirectories { get; set; } = null!;

    public Site Site { get; set; } = null!;

    public ApplicationPool ApplicationPool { get; set; } = null!;
}

public class Site
{
    public int Id { get; set; }

    public string Name { get; set; } = null!;

    public string Path { get; set; } = null!;

    public string ApplicationPoolName { get; set; } = null!;

    public ApplicationPool ApplicationPool { get; set; } = null!;

    public string EnabledProtocols { get; set; } = null!;

    public List<Application> Applications { get; set; } = null!;
}
roji commented 6 months ago

I actually don't need to map to JSON at all. I just need navigation and tracking.

How exactly are you looking to map the collection to the databases? If a Blog has a collection of Posts, how are the Posts represented in the database? When mapping to JSON, those Posts could be stored as a single column containing a JSON array, but if you're not looking for JSON, then how would you map this? If you'd have a separate Posts table where each row represents a Post, then why are you looking for complex types specifically and not just using regular entity types - or owned?

I will admit Owned Types are one of the things I have not explored as of yet.

I'd recommend doing this and thinking about the conceptual difference between owned types, complex types and regular entity types.

ajcvickers commented 6 months ago

Someone missed a golden opportunity to respond with just, "It's complex..." ;-)

rlcarrierscsharp commented 6 months ago

Someone missed a golden opportunity to respond with just, "It's complex..." ;-)

It's complex... very complex :) I'm kicking myself for not reading up on owned types. I am able to get further with using them - is there a better way to continue this discussion if I have further questions / comments, as to not clutter this PR? Once again, thanks for all of the replies. I know you all are very busy and even getting back to me is nothing short of amazing. I have great respect for the awesome design you all have created!

roji commented 6 months ago

@rlcarrierscsharp if you have further questions for understanding the design, it's definitely better to open a different issue to keep that discussion separate. I'll go ahead and hide this discussion (which isn't relevant per se to this issue).

ajcvickers commented 4 months ago

Note: consider #33913 when working on complex type collections.