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

Throw when non-owned type is derived from an owned type. #17429

Closed
nvsdt opened this issue Aug 26, 2019 · 2 comments · Fixed by #17470
Closed

Throw when non-owned type is derived from an owned type. #17429

nvsdt opened this issue Aug 26, 2019 · 2 comments · Fixed by #17470
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@nvsdt
Copy link

nvsdt commented Aug 26, 2019

Hello,
I have two similar pieces of code. The first works correctly. The second throws an exception. The difference is only in the order of entity registration (OnModelCreating).

Exception message: 

A key cannot be configured on 'EntityTwo' because it is a derived type. The key must be configured on the root type 'ValueObject'. If you did not intend for 'ValueObject' to be included in the model, ensure that it is not included in a DbSet property on your contex
t, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.

Stack trace:
System.InvalidOperationException: A key cannot be configured on 'EntityTwo' because it is a derived type. The key must be configured on the root type 'ValueObject'. If you did not intend for 'ValueObject' to be included in the model, ensure that it is not included
in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.
   at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.AddKey(IReadOnlyList`1 properties, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasKeyInternal(IReadOnlyList`1 properties, Nullable`1 configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.PrimaryKey(IReadOnlyList`1 properties, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.HasKey(Expression`1 keyExpression)
   at Example.SomeDbContext.OnModelCreating(ModelBuilder modelBuilder) in C:\Src\templates\Example\Example\Example.cs:line 38
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
   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.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

Steps to reproduce

dotnet ef migrations add Init

Correctly

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace Example
{
    public class DesignTimeStoreDbContextFactory : IDesignTimeDbContextFactory<SomeDbContext>
    {
        public SomeDbContext CreateDbContext(string[] args)
        {
            return new SomeDbContext(
                new DbContextOptionsBuilder()
                    .UseSqlServer("connection_string")
                    .Options);
        }
    }
    
    public class SomeDbContext : DbContext
    {
        public SomeDbContext(DbContextOptions options)
            : base(options)
        {
        }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //entity one
            var builderForEntityOne = modelBuilder.Entity<EntityOne>();
            builderForEntityOne.HasKey(e => e.Id);
            
            //entity two
            var builderForEntityTwo = modelBuilder.Entity<EntityTwo>();
            builderForEntityTwo.HasKey(e => e.Id);
            
            //project of entity
            var builderForProjectOfEntity = modelBuilder.Entity<EntityProject>(); 
            builderForProjectOfEntity.HasKey(e => e.Id);
            builderForProjectOfEntity.OwnsOne(e => e.ValueObject);
        }
    }

    public class ValueObject
    {
        public int Value { get; set; }
    }

    public class EntityOne : ValueObject
    {
        public Guid Id { get; set; }
    }
    
    public class EntityTwo : ValueObject
    {
        public Guid Id { get; set; }
    }
    
    public class EntityProject
    {
        public Guid Id { get; set; }
        public ValueObject ValueObject { get; set; }
    }
}

Exception

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace Example
{
    public class DesignTimeStoreDbContextFactory : IDesignTimeDbContextFactory<SomeDbContext>
    {
        public SomeDbContext CreateDbContext(string[] args)
        {
            return new SomeDbContext(
                new DbContextOptionsBuilder()
                    .UseSqlServer("connection_string")
                    .Options);
        }
    }
    
    public class SomeDbContext : DbContext
    {
        public SomeDbContext(DbContextOptions options)
            : base(options)
        {
        }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //entity one
            var builderForEntityOne = modelBuilder.Entity<EntityOne>();
            builderForEntityOne.HasKey(e => e.Id);
            
            //project of entity
            var builderForProjectOfEntity = modelBuilder.Entity<EntityProject>(); 
            builderForProjectOfEntity.HasKey(e => e.Id);
            builderForProjectOfEntity.OwnsOne(e => e.ValueObject);
            
            //entity two
            var builderForEntityTwo = modelBuilder.Entity<EntityTwo>();
            builderForEntityTwo.HasKey(e => e.Id);
        }
    }

    public class ValueObject
    {
        public int Value { get; set; }
    }

    public class EntityOne : ValueObject
    {
        public Guid Id { get; set; }
    }
    
    public class EntityTwo : ValueObject
    {
        public Guid Id { get; set; }
    }
    
    public class EntityProject
    {
        public Guid Id { get; set; }
        public ValueObject ValueObject { get; set; }
    }
}

Further technical details

EF Core version: 2.2.4
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: JetBrains Rider 2019.2

@smitpatel
Copy link
Member

The model is invalid in EF core. First one should also be throwing exception. A regular entityType cannot be derived from an owned entityType.

@ajcvickers
Copy link
Member

Model built in 3.0 for first case:

Model: 
  EntityType: EntityOne
    Properties: 
      Id (Guid) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Value (int) Required
    Keys: 
      Id PK
  EntityType: EntityProject
    Properties: 
      Id (Guid) Required PK AfterSave:Throw ValueGenerated.OnAdd
    Navigations: 
      ValueObject (<ValueObject>k__BackingField, ValueObject) ToDependent ValueObject
    Keys: 
      Id PK
  EntityType: EntityTwo
    Properties: 
      Id (Guid) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Value (int) Required
    Keys: 
      Id PK
  EntityType: ValueObject
    Properties: 
      EntityProjectId (no field, Guid) Shadow Required PK FK AfterSave:Throw
      Value (int) Required
    Keys: 
      EntityProjectId PK
    Foreign keys: 
      ValueObject {'EntityProjectId'} -> EntityProject {'Id'} Unique Ownership ToDependent: ValueObject
Annotations: 
  ProductVersion: 3.0.0-rc1.19422.8

@ajcvickers ajcvickers added this to the 3.1.0 milestone Aug 26, 2019
@AndriySvyryd AndriySvyryd changed the title A key cannot be configured on '' because it is a derived type. Throw when non-owned type is derived from an owned type. Aug 26, 2019
AndriySvyryd added a commit that referenced this issue Aug 28, 2019
AndriySvyryd added a commit that referenced this issue Aug 28, 2019
AndriySvyryd added a commit that referenced this issue Aug 28, 2019
AndriySvyryd added a commit that referenced this issue Aug 28, 2019
AndriySvyryd added a commit that referenced this issue Aug 28, 2019
@AndriySvyryd AndriySvyryd added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported labels Aug 29, 2019
@AndriySvyryd AndriySvyryd removed their assignment Aug 29, 2019
@ajcvickers ajcvickers modified the milestones: 3.1.0, 3.1.0-preview1 Oct 15, 2019
@ajcvickers ajcvickers modified the milestones: 3.1.0-preview1, 3.1.0 Dec 2, 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. customer-reported type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants