Faegav
Faegav

Reputation: 51

Is it possible to use an owned type for many-to-many relations?

In Entity Framework Core version 2.2 or 3.0, is it possible to use owned/complex types in such a way that this kind of configuration is possible:

public class Product {

   public int Id { get; set; }
   public string Name { get; set; }
   public ProductProperties Properties { get; set; }
}

public class ProductProperties {

       public List<ProductSize> Sizes { get; set; }
}

public class Size {

       public int Id { get; set; }
       public string Name { get; set; }
}

public class ProductSize {

       public int ProductId { get; set; }
       public Product Product { get; set; }

       public int SizeId { get; set; }
       public Size Size { get; set; }
}

modelBuilder.Entity<ProductSize>()
                .HasOne(x => x.Product)
                .WithMany(x => x.Properties.Sizes)
                .HasForeignKey(x => x.ProductId);

modelBuilder.Entity<ProductSize>()
                .HasOne(x => x.Size)
                .WithMany()
                .HasForeignKey(x => x.SizeId);

The error message which is seen for this kind of approach usually ends up in:

'x => x.Properties.Sizes' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'

An earlier found answer is almost exactly matching my question, but this was posted in 2013. By the time it was almost certainly not possible.

HasForeignKey relationship through a Complex Type property

The sources on Microsoft are only giving examples for creating an entity with the complex type itself, not for creating relationships between them.

Upvotes: 3

Views: 1252

Answers (2)

Iliass Akkara
Iliass Akkara

Reputation: 671

The cause of the issue

In your sample code it's quite clear there is no specific Many to Many relation. To make my argument a bit more convincing what follows is a model of your entities and their relations:

enter image description here

The new class structure

For a Many to Many relation to work in EF the product and size tables need to have an implicit relation with each other through a singular junction table. In my proposed solution I've chosen the ProductProperty table. There I've added the fields from the productsize junction table:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<ProductProperty> Properties { get; set; }
}

public class ProductProperty
{
    public int ProductId { get; set; }
    public Product Product { get; set; }

    public int SizeId { get; set; }
    public Size Size { get; set; }
}

public class Size 
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<ProductProperty> Properties { get; set; }
}

The functions

modelBuilder.Entity<ProductProperty>()
    .HasKey(pp => new { pp.ProductId, pp.SizeId });

modelBuilder.Entity<ProductProperty>()
    .HasOne(pp => pp.Product)
    .WithMany(p => p.Properties)
    .HasForeignKey(pp => pp.ProductId);

modelBuilder.Entity<ProductProperty>()
    .HasOne(pp => pp.Size)
    .WithMany(p => p.Properties)
    .HasForeignKey(pp => pp.SizeId);

Additional advice (EDIT)

Make the "Size" class a generic property class. This way the Many-to-Many relation won't get broken and querying will also be very easy:

public class Property
{
    public int Id { get; set; }
    public PropertyType propType { get; set; }
    public string propValue { get; set; }
}

public enum PropertyType
{
    Size,
    Fontsize,
    ...
}

As a final argument this change will make it easier to change existing properties or add new ones

Sources

  1. https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

Upvotes: 2

Athanasios Kataras
Athanasios Kataras

Reputation: 26450

You can check the owned entity types released in 2019 Check documentation here

An example from the link is the following:

public class Distributor
{
    public int Id { get; set; }
    public ICollection<StreetAddress> ShippingCenters { get; set; }
}

The owns many function should help you like this:

modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a =>
{
    a.WithOwner().HasForeignKey("OwnerId");
    a.Property<int>("Id");
    a.HasKey("Id");
});

Let me know if I misunderstood your question.

Upvotes: 0

Related Questions