EF Core 7.0 - OwnsOne 在内部抛出 NRE。

3

使用 EntityFrameworkCore v7.0.0(带有 SQL Server 适配器)

我正在尝试通过调用 .OwnsOne(...) 将一个拥有的实体附加到主实体,但是 EFCore 在尝试生成阴影外键时在内部生成了一个 NullReferenceException

代码如下:

public class ProductId : ValueObject
{
    public Guid Value { get; private set; }

    private ProductId(Guid value)
    {
        Value = value;
    }

    public static ProductId Create(Guid value)
    {
        return new ProductId(value);
    }
}

public class ProductName : ValueObject 
{
    public string Value { get; private set; }

    public ProductName(string value)
    {
        Value = value;
    }

    // ValueObject overrides
}

public abstract class AggregateRoot<TId> where TId : notnull
{
    // ... ctor
}

public class Product : AggregateRoot<ProductId>
{
    private Product() { }

    public ProductName Name { get; private set; }
}

还有以下实现IEntityTypeConfiguration<>

public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.ToTable("Products");

        builder.HasKey(p => p.Id);

        builder.Property(p => p.Id)
            .ValueGeneratedNever()
            .HasConversion(
                id => id.Value,
                value => ProductId.Create(value));

        builder.OwnsOne(
            p => p.Name,
            navBuilder =>
            {
                navBuilder.Property(x => x.Value)
                    .HasColumnName(nameof(Product.Name))
                    .IsRequired();
            });
    }
}

EFCore堆栈跟踪NRE的发生:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.CreateForeignKey(InternalEntityTypeBuilder principalEntityTypeBuilder, IReadOnlyList`1 dependentProperties, Key principalKey, String propertyBaseName, Nullable`1 required, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasRelationship(EntityType targetEntityType, Nullable`1 navigationToTarget, Nullable`1 inverseNavigation, Nullable`1 setTargetAsPrincipal, ConfigurationSource configurationSource, Nullable`1 required)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasOwnership(EntityType targetEntityType, MemberIdentity& navigation, Nullable`1 inverse, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Microsoft.EntityFrameworkCore.Metadata.Builders.IConventionEntityTypeBuilder.HasOwnership(IConventionEntityType targetEntityType, MemberInfo navigation, Boolean fromDataAnnotation)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.RelationshipDiscoveryConvention.CreateRelationships(IEnumerable`1 relationshipCandidates, IConventionEntityTypeBuilder entityTypeBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.RelationshipDiscoveryConvention.Process(IConventionEntityType entityType, String navigationName, MemberInfo memberInfo, IConventionContext context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.RelationshipDiscoveryConvention.ProcessNavigationRemoved(IConventionEntityTypeBuilder sourceEntityTypeBuilder, IConventionEntityTypeBuilder targetEntityTypeBuilder, String navigationName, MemberInfo memberInfo, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnNavigationRemoved(IConventionEntityTypeBuilder sourceEntityTypeBuilder, IConventionEntityTypeBuilder targetEntityTypeBuilder, String navigationName, MemberInfo memberInfo)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnNavigationRemovedNode.Run(ConventionDispatcher dispatcher)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.DelayedConventionScope.Run(ConventionDispatcher dispatcher)
   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](TypeIdentity ownedType, MemberIdentity navigation)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.OwnsOne[TRelatedEntity](Expression`1 navigationExpression, Action`1 buildAction)
   at CustomNamespace.Persistence.ProductConfiguration.Configure(EntityTypeBuilder`1 builder) in C:\CustomNamespace\Persistence\ProductConfiguration.cs:line 27
   at Microsoft.EntityFrameworkCore.ModelBuilder.ApplyConfiguration[TEntity](IEntityTypeConfiguration`1 configuration)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.EntityFrameworkCore.ModelBuilder.ApplyConfigurationsFromAssembly(Assembly assembly, Func`2 predicate)
   at CustomNamespace.Persistence.DbContext.OnModelCreating(ModelBuilder modelBuilder) in C:\CustomNamespace\Persistence\DbContext.cs:line 16
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.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_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.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, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Exception has been thrown by the target of an invocation.

我尝试通过EFCore OwnsOne实现进行研究和调试,但是我没有找到原因。我有其他遵循相同逻辑的实体,一切都按预期工作。


不要在你的应用程序中使用可能根本不需要的概念,尝试首先仅使用EF Core。现在你正在向业务层泄漏ORM概念(因为你加载了ORM实体),并将业务概念下传到ORM。 - Panagiotis Kanavos
1
我想提供一些代码,领域驱动设计原则是否应用于域层并不重要。重要的是,当尝试将ProductName拥有的实体附加到主实体Product时,OwnsOne抛出了NullReferenceException异常。 - marinobjelopera
如果这样一个基本的功能无法正常工作,所有使用 EF Core 的开发人员都会注意到,所有示例也将无法正常工作。ProductName 不是实体,它只是一个属性。您仍在尝试让 ORM 像 DDD 一样工作,使用错误的术语和函数来处理错误的事情。删除所有尝试使用 DDD 术语的代码,例如 ValueObjectAggregateRoot 类,使用普通的 C# 对象,并检查 OwnsOne 是否正常工作-假设这有意义。只有在那之后才尝试使事情变得更加花哨。 - Panagiotis Kanavos
1
我应该能够将ProductName值对象作为拥有类型进行持久化 - 即将Product.ProductName.Value映射到SQL表[Products]的[Name]列。我不明白我的领域层结构有什么重要性。我并不是说EFCore的基本功能不起作用 - 只是在这种特定情况下它对我不起作用,尽管我有其他实现相同原则的实体,它们按预期工作。 - marinobjelopera
测试驱动开发是DDD的一个关键概念,而现在这正好相反。与其试图获得可工作的代码并对其进行修改,问题从末尾开始,即失败的、复杂的代码。从一个简单的DbContext和不继承任何东西的实体开始。确保它编译并通过单元测试。为此,您可以使用内存中的EF Core提供程序,而不是数据库。一旦这样做成功了,就添加一个基类,只在真正需要时使用。让测试通过。然后尝试使用自定义类型而不是简单值。 - Panagiotis Kanavos
显示剩余2条评论
2个回答

1
花了19天才弄清楚这个问题。我可能是错的。在我的情况下,我试图定义一个拥有多个角色的用户,并且遇到了相同的错误。然后今天,在将“User”更改为“FarmUser”,将“Role”更改为“FarmRole”之后,问题得以解决。有可能反射首先会尝试解析预定义的内容。

这是一种可能性。我尝试在新创建的解决方案中复现此问题,但无法使用相同的配置进行复现。真的不理解是什么引起了这个问题。 - marinobjelopera

0

我在EF GitHub上找到了相关问题,并成功地重现了这个问题。当CLR类型被定义为实体X的属性,同时又作为实体Y的单个(或集合)拥有实体时,就会出现这个问题。

例如:

public class FooId 
{
    public Guid Value { get; private set; }
}

public class BarId 
{
    public Guid Value { get; private set; }
}

public class Foo
{
    private Foo()
    {
    }
    
    public FooId Id { get; private set; }

    public FooDetail Detail { get; private set; }
}

public class FooDetail 
{
    public string Value { get; private set; }

    private FooDetail()
    {
    }
}

public class Bar
{
    private readonly List<FooId> _fooIds = new();

    private Bar()
    {
    }

    public BarId Id { get; private set; }

    public IReadOnlyCollection<FooId> FooIds => _fooIds.AsReadOnly();
}


public class FooConfiguration : IEntityTypeConfiguration<Foo>
{
    public void Configure(EntityTypeBuilder<Foo> builder)
    {
        builder.ToTable("Foos");

        builder.HasKey(f => f.Id);
        
        builder.Property(f => f.Id)
            .ValueGeneratedNever()
            .HasConversion(
                id => id.Value,
                value => FooId.Create(value));

        // This line will throw NRE because:
        // FooId is used as a property on Foo
        // And FooId is used as a collection of owned entities on Bar
        builder.OwnsOne(
            f => f.Detail,
            dBuilder => 
            {
                dBuilder.Property(d => d.Value)
                    .IsRequired()
                    .HasColumnName("Detail")
                    .HasMaxLength(100);
            });
    }
}

public class BarConfiguration : IEntityTypeConfiguration<Bar>
{
    public void Configure(EntityTypeBuilder<Bar> builder)
    {
        builder.ToTable("Bars");

        builder.HasKey(f => f.Id);
        
        builder.Property(f => f.Id)
            .ValueGeneratedNever()
            .HasConversion(
                id => id.Value,
                value => FooId.Create(value));

        builder.OwnsMany(
            b => b.FooIds,
            fBuilder => 
            {
                fBuilder.ToTable("BarFooIds");

                fBuilder.WithOwner().HasForeignKey("BarId");

                fBuilder.HasKey("Id");

                fBuilder.Property(f => f.Value)
                    .ValueGeneratedNever()
                    .HasColumnName(nameof(FooId));
            });

        builder.MetaData
            .FindNavigation(nameof(Bar.FooIds))!
            .SetPropertyAccessMode(PropertyAccessMode.Field);
    }
}

相关的 GitHub 问题:

EF 问题 30203

EF 问题 30373


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接