Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with owned types mapping in EF Core 3.0

I've migrated from EF Core Preview5 to Preview7 and now I have same internal complex properties mapping through select.

For example:

public class Car
{
    public Volume Volume { get; set; }
    public string OtherProperty { get; set; }
}

[Owned]
public class Volume
{
    public float Height { get; set; }
    public float Width { get; set; }
    public float Length { get; set;}
}

Earlier, the code modelBuilder.Entity<Car>().OwnsOne(e => e.Volume) worked properly, but now it needs to use WithOwner but I can't understand (see here: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes) I can't use code like this: modelBuilder.Entity<Car>().OwnsOne(e => e.Volume).WithOwner("Car") or modelBuilder.Entity<Car>().OwnsOne(e => e.Volume).WithOwner(f => f.Car). Does anyone have the same problem?

Thanks.

Update.

I've checked OrderStoreDbContextModelSnapshot.cs. I've posted here other example fully congruent with the upper example.

modelBuilder.Entity("DatabaseServiceNew.Database.Order_information.OrderProfile", b =>
            {
                b.HasOne("DatabaseService.Database.Order_information.Order", "Order")
                    .WithOne("OrderProfile")
                    .HasForeignKey("DatabaseServiceNew.Database.Order_information.OrderProfile", "OrderId")
                    .OnDelete(DeleteBehavior.Cascade)
                    .IsRequired();

                b.OwnsOne("FoundationClasses.Technical_Classes.Volume", "Volume", b1 =>
                    {
                        b1.Property<Guid>("OrderProfileId");

                        b1.Property<float>("Cum");

                        b1.Property<float>("Height");

                        b1.Property<float>("Length");

                        b1.Property<float>("Width");

                        b1.HasKey("OrderProfileId");

                        b1.ToTable("OrderProfiles");

                        b1.WithOwner()
                            .HasForeignKey("OrderProfileId");
                    });

                b.OwnsOne("WebFoundationClassesCore.Data_classes.GeoPoint", "EndPoint", b1 =>
                    {
                        b1.Property<Guid>("OrderProfileId");

                        b1.Property<string>("Address");

                        b1.Property<double>("Latitude");

                        b1.Property<double>("Longitude");

                        b1.HasKey("OrderProfileId");

                        b1.ToTable("OrderProfiles");

                        b1.WithOwner()
                            .HasForeignKey("OrderProfileId");
                    });

                b.OwnsOne("WebFoundationClassesCore.Data_classes.GeoPoint", "StartPoint", b1 =>
                    {
                        b1.Property<Guid>("OrderProfileId");

                        b1.Property<string>("Address");

                        b1.Property<double>("Latitude");

                        b1.Property<double>("Longitude");

                        b1.HasKey("OrderProfileId");

                        b1.ToTable("OrderProfiles");

                        b1.WithOwner()
                            .HasForeignKey("OrderProfileId");
                    });
            });

where

[Owned, ComplexType]
public class Volume
{
    public float Height { get; set; }
    public float Width { get; set; }
    public float Length { get; set;}
}


[Owned, ComplexType]
public class GeoPoint 
{
    public GeoPoint() 
    {
    }
    public GeoPoint(double latitude, double longitude, string address) 
    {
        this.Address = address;
        this.Latitude = latitude;
        this.Longitude = longitude;
    }

    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public string Address { get; set;}
}

So, as we can see, ContextSnapshot maps data correctly (ComplexType attribute do nothing in real in this case, experimentally).

OrderStoreDbContext has public DbSet<OrderProfile> OrderProfiles { get; set; } property.

But linq request var orderProfiles = await orderDbContext.OrderProfiles.ToListAsync(); maps just simple types (which are exist in the OrderProfiles table, but not complex. var orderProfiles = await orderDbContext.OrderProfiles.Include(p => p.Volume).ToListAsync(); code also has no effect - I get orderProfiles.Volume and orderProfiles.StartPoint and orderProfiles.EndPoint values as null.

But, in the Preview5 this code works fine. Has microsoft developers broken complex type mapping in EF Core 3.0 Preview7 or the problem in my curved hands?

Update 2. Posted an issue on github project repo.

like image 756
Dmitriy Avatar asked Jul 26 '19 10:07

Dmitriy


People also ask

Is EF core faster than EF6?

Entity Framework (EF) Core, Microsoft's object-to-database mapper library for . NET Framework, brings performance improvements for data updates in version 7, Microsoft claims. The performance of SaveChanges method in EF7 is up to 74% faster than in EF6, in some scenarios.

Should I use EF6 or EF core?

Keep using EF6 if the data access code is stable and not likely to evolve or need new features. Port to EF Core if the data access code is evolving or if the app needs new features only available in EF Core. Porting to EF Core is also often done for performance.

Does EF core support EDMX?

EF Core does not support the EDMX file format for models. The best option to port these models, is to generate a new code-based model from the database for your application.

What is an owned entity?

Owned entities are essentially a part of the owner and cannot exist without it, they are conceptually similar to aggregates. This means that the owned entity is by definition on the dependent side of the relationship with the owner.


1 Answers

WithOwner fluent API is still undocumented (normal for preview software), but it follows the relationship API (HasOne / HasMany / WithOne, WithMany) pattern for navigation property - if you have navigation property, pass either lambda expression or the name of the property (string)). If you don't have navigation property, don't pass anything.

You can see that for one of the WithOwner overloads using Go To Definition Command is VS:

//
// Summary:
//     Configures the relationship to the owner.
//     Note that calling this method with no parameters will explicitly configure this
//     side of the relationship to use no navigation property, even if such a property
//     exists on the entity type. If the navigation property is to be used, then it
//     must be specified.
//
// Parameters:
//   ownerReference:
//     The name of the reference navigation property pointing to the owner. If null
//     or not specified, there is no navigation property pointing to the owner.
//
// Returns:
//     An object that can be used to configure the relationship.
public virtual OwnershipBuilder<TEntity, TDependentEntity> WithOwner([CanBeNullAttribute] string ownerReference = null);

Same is shown by VS Intellisense.

So in your case, just use WithOwner(), e.g.

modelBuilder.Entity<Car>().OwnsOne(e => e.Volume).WithOwner()
    . /* configuration goes here */
like image 180
Ivan Stoev Avatar answered Oct 28 '22 07:10

Ivan Stoev