Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conventions shouldn't try to set the base type for owned types #9536

Closed
bubibubi opened this issue Aug 23, 2017 · 7 comments
Closed

Conventions shouldn't try to set the base type for owned types #9536

bubibubi opened this issue Aug 23, 2017 · 7 comments
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@bubibubi
Copy link

For example an owned type Address { Street : string, City : City } and an owned type City { Name : string , State : string}

@ajcvickers
Copy link
Contributor

@bubibubi Are you asking for inheritance of containment? If the former, can you add a bit more detail as to what the types look like and how you expect them to be mapped? If the latter, then you can already do:

public class Foo
{
    public int Id { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public City City { get; set; }
}

public class City
{
    public string Name { get; set; }
    public string State { get; set; }
}

modelBuilder.Entity<Foo>()
    .OwnsOne(e => e.Address)
    .OwnsOne(e => e.City);

@bubibubi
Copy link
Author

@ajcvickers I'd like to map something like this

    public class Friend
    {
        public Friend()
        {
            Address = new FullAddress();
        }

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

        public FullAddress Address { get; set; }
    }

    public class LessThanFriend
    {
        public LessThanFriend()
        {
            Address = new CityAddress();
        }

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

        public CityAddress Address { get; set; }
    }


    public class CityAddress
    {
        public string Cap { get; set; }
        public string City { get; set; }
    }


    public class FullAddress : CityAddress
    {
        public string Street { get; set; }
    }



    public class TestContext : DbContext
    {
        public TestContext(DbContextOptions options) : base(options) { }

        public DbSet<Friend> Friends { get; set; }
        public DbSet<LessThanFriend> LessThanFriends { get; set; }

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

            // This Address is a full address. The problem is here
            modelBuilder.Entity<Friend>()
                .OwnsOne(_ => _.Address);
            // This Address is a city address
            modelBuilder.Entity<LessThanFriend>()
                .OwnsOne(_ => _.Address);
        }
    }

@smitpatel
Copy link
Contributor

BaseTypeDiscoveryConvention shouldn't be running on OwnedTypes since they don't allow inheritance.
behavior in 2.0 using above code.

Unhandled Exception: System.InvalidOperationException: The dependent entity type 'Friend.Address#FullAddress' cannot have a base type.
   at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.HasBaseType(EntityType entityType, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasBaseType(EntityType baseEntityType, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.BaseTypeDiscoveryConvention.Apply(InternalEntityTypeBuilder entityTypeBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnEntityTypeAdded(InternalEntityTypeBuilder entityTypeBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.RunVisitor.VisitOnEntityTypeAdded(OnEntityTypeAddedNode node)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnEntityTypeAddedNode.Accept(ConventionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionVisitor.Visit(ConventionNode node)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionVisitor.VisitConventionScope(ConventionScope node)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionScope.Accept(ConventionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionVisitor.Visit(ConventionNode node)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionVisitor.VisitConventionScope(ConventionScope node)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Run()
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Dispose()
   at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.OwnsOneBuilder[TRelatedEntity](PropertyInfo navigation)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.OwnsOne[TRelatedEntity](Expression`1 navigationExpression)
   at EFSampleApp.MyContext.OnModelCreating(ModelBuilder modelBuilder) in D:\code\EFSampleApp\EFSampleApp\Program.cs:line 55
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass5_0.<GetModel>b__0(Object k)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_1(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass22_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.get_DatabaseCreator()
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureDeleted()

@AndriySvyryd

@AndriySvyryd AndriySvyryd changed the title Support for owned types inherithed from other owned types Conventions shouldn't try to set the base type for owned types Aug 24, 2017
@AndriySvyryd AndriySvyryd self-assigned this Aug 24, 2017
@ajcvickers
Copy link
Contributor

@bubibubi Couple of additional questions:

  • Are you expecting that you can assign a FullAddress object to LessThanFiend.CityAddress and have it correctly persisted to the database?
  • What do you expect that table columns to look like for the LessThanFriend table?

@bubibubi
Copy link
Author

@ajcvickers in general I'm expecting that EF handles a hierarchy of classes as a single class.

About first question, I'm not expecting that all the properties of the assigned object are persisted.
Actually it does not work in that way (EF persists the properties of the class specified in OwnsOne and I think that it is a perfect behaviour).

About the second question I'm expecting that the columns of LessThanFriend are able to contain the properties of the class specified in OwnsOne (so the properties of CityAddress). In my example I called both the properties Address and is a little bit confusing.

Here the tables I'm expecting.

CREATE TABLE [Friends] (
 [Id] int not null identity(1,1),
 [Name] text null,
 [Address_Street] text null,
 [Address_Cap] text null,
 [Address_City] text null,
);

CREATE TABLE [LessThanFriends] (
 [Id] int not null identity(1,1),
 [Name] text null,
 [Address_Cap] text null,
 [Address_City] text null
);

@ajcvickers
Copy link
Contributor

Triage decision: for now, we will throw a better exception. Issue #9630 filed for future to actual map these types.

@bubibubi Thanks for the great feedback. We're not planning to support this now--see #9630--but you can workaround the issue by using only leaves for your owned types. For example:

public class AddressBase
{
    public string Cap { get; set; }
    public string City { get; set; }
}

public class CityAddress : AddressBase
{
}

public class FullAddress : AddressBase
{
    public string Street { get; set; }
}

@AndriySvyryd
Copy link
Member

Fixed in 2507a80

@AndriySvyryd AndriySvyryd added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Feb 6, 2018
@AndriySvyryd AndriySvyryd removed their assignment Feb 6, 2018
@ajcvickers ajcvickers modified the milestones: 2.1.0-preview2, 2.1.0 Nov 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants