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.82k stars 3.2k forks source link

Support complex properties on Owned types #33170

Open lzoller-flexibilIT opened 9 months ago

lzoller-flexibilIT commented 9 months ago

File a bug

I'm trying to set up a database containing different tables that have to be implemented as ComplexType or Owned respectively. I'm using attributes to configure that.

The ef command line tool succussfully creates a migration for the model but the generated code contains errors for every occurence of a ComplexType within an Owned type because the class OwnedNavigationBuilder doesn't contain a method for ComplexType.

I can't make everything Owned because of missing relations and I can't make everything ComplexType because of a 1:n relation from another table.

As the ef tool successfully generates the migration code and accepts the model this must be a bug in EFcore. The migration code generated keeps the project from building so e.g. ef migrations remove will fail, the migration classes have to be removed manually.

Include your code

Here the relevant snippets from the model.

Starting point is the Invoice class containing a list of LineItems:

    public class Invoice
    {
        public Company customer { get; set; }
        public Project project { get; set; }
        ...
        public List<LineItem> line_items { get; set; }
    }

The LineItem is Owned and contains a Proposition:

    [Owned]
    public class LineItem
    {
        public string? LineItemType { get; set; }
        public string? Name { get; set; }
        public string? Description { get; set; }
        ...
        public Proposition Proposition { get; set; }
    }

Finally the Proposition is a ComplexType:

    [ComplexType]
    public class Proposition
    {
        public string? Type { get; set; }
        public int? Id { get; set; }
        public string? Name { get; set; }
        ...
    }

Creating a migration based on this model via...

dotnet ef migrations add Initial

...is successful:

Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'

Include stack traces

(no execution possible)

Include verbose output

The migration classes produced looks like this (the complier error occurs at b1.ComplexProperty<>):

            ...
            modelBuilder.Entity("MyProject.Model.Invoice", b =>
                {
                    ...
                    b.HasOne("PapierkramShared.Model.Company", "customer")
                        .WithMany()
                        .HasForeignKey("customerId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("PapierkramShared.Model.Project", "project")
                        .WithMany()
                        .HasForeignKey("projectId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.OwnsMany("MyProject.Model.LineItem", "line_items", b1 =>
                        {
                            ...
                            b1.Property<string>("LineItemType")
                                .HasColumnType("nvarchar(max)")
                            b1.Property<string>("Name")
                                .HasColumnType("nvarchar(max)")
                            b1.Property<string>("Description")
                                .HasColumnType("nvarchar(max)")
                            ...
                            b1.ComplexProperty<Dictionary<string, object>>("Proposition", "MyProject.Model.InvoiceDetails.line_items#LineItem.Proposition#Proposition", b2 =>
                                {
                                    b2.IsRequired();
                                    b2.Property<string>("Type")
                                        .IsRequired()
                                        .HasColumnType("nvarchar(max)")
                                    b2.Property<int>("Id")
                                        .HasColumnType("int")
                                    b2.Property<string>("Name")
                                        .IsRequired()
                                        .HasColumnType("nvarchar(max)")
                                    ...

The compiler error produces is this one (translated from German): error CS1061: "OwnedNavigationBuilder" does not contain a definition for "ComplexProperty" and no accessible extension method "ComplexProperty" accepting a first argument of type "OwnedNavigationBuilder" could be found.

Include provider and version information

EF Core version: 8.0.2 EF Core Command-line Tools: 8.0.2 Database provider: Microsoft.EntityFrameworkCore.SqlServer / also tried: Npgsql.EntityFrameworkCore.PostgreSQL Target framework: NET 8.0 Operating system: Windows 11 IDE: Visual Studio 2022 17.9.0

JoasE commented 9 months ago

I was about to create this issue myself! However, I was going to make it a feature request as I was using the fluent api to configure my entity relations and noticed the ComplexProperty builder method missing on the OwnedNavigationBuilder.

Should I create a separate issue for this? As the fact that the migrations generator is generating code that has a compile time error is an issue of itself, but it could be fixed by implementing functionality.

My code:

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

    public OwnedEntity OwnedEntity { get; set; }

    public ComplexType ComplexType { get; set; }
}

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

    public ComplexType ComplexType { get; set; }
}

public class ComplexType
{
    public string Name { get; set; }

    public string Description { get; set; }
}

class TestDbContext : DbContext
{
    public TestDbContext() : base(new DbContextOptionsBuilder<TestDbContext>().UseSqlServer().Options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Entity>(cfg =>
        {
            cfg.ComplexProperty(x => x.ComplexType);

            cfg.OwnsOne(x => x.OwnedEntity, cfg =>
            {
                // Method to configure ComplexProperty does not exist!
                cfg.ComplexProperty(x => x.ComplexType); // Error CS1061  'OwnedNavigationBuilder<Entity, OwnedEntity>' does not contain a definition for 'ComplexProperty' and no accessible extension method 'ComplexProperty' accepting a first argument of type 'OwnedNavigationBuilder<Entity, OwnedEntity>' could be found
            });
        });
    }
}
lzoller-flexibilIT commented 9 months ago

Hi @JoasE, that's curious :-) Creating your own issue sounds good. Thanks for hint with the fluent api and the sample code - even though I was trying to prevent using it...

ajcvickers commented 9 months ago

@AndriySvyryd Do you know if we are already tracking this?

AndriySvyryd commented 9 months ago

It looks like I missed creating an issue for this