繁体   English   中英

EF Core DDD ValueObject 属性是键的一部分,因此不能修改或标记为已修改

EF Core DDD ValueObject property is part of a key and so cannot be modified or marked as modified

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

您好,我有时在保存实体时遇到并行集成测试问题。 EF Core 引发异常并显示消息:属性“Order.Price#Money.OrderId”是键的一部分,因此无法修改或标记为已修改。

真的很好奇我运行单个测试'should_get_order'并且一切都很好,但是当我尝试运行所有测试时有时我会遇到问题

这是实现

public class Order : AggregateRoot
{
    // some other props
    public Money Price { get; private set; }

    public Order() 
    {
        Price = Money.Zero; // exception throws but when I change to new Money(0) everything works fine
    }

    public static Order Create() 
    {
        return new Order();
    }
}

public abstract class AggregateRoot
{
    public AggregateId Id { get; protected set; }
}

public class AggregateId<T> : IEquatable<AggregateId<T>>
{
    public T Value { get; }

    public AggregateId(T value)
    {
        Value = value;
    }

    public bool Equals(AggregateId<T>? other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return EqualityComparer<T>.Default.Equals(Value, other.Value);
    }

    public override bool Equals(object? obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((AggregateId<T>)obj);
    }

    public override int GetHashCode()
    {
        return EqualityComparer<T>.Default.GetHashCode(Value);
    }
}

public class AggregateId : AggregateId<Guid>
{
    public AggregateId() : this(Guid.NewGuid())
    {
    }

    public AggregateId(Guid value) : base(value)
    {
    }

    public static implicit operator Guid(AggregateId id) => id.Value;
    public static implicit operator AggregateId(Guid id) => new(id);
}

public class Money : IEquatable<Money>
{
    public static readonly Money Zero = new Money(0);
    public decimal Value { get; }

    public Money(decimal value)
    {
        if (value < 0)
        {
            throw new InvalidOperationException($"Money '{value}' cannot be negative");
        }

        Value = value;
    }

    public Money ChangeValue(decimal value)
    {
        return new Money(value);
    }

    public bool Equals(Money? other)
    {
        if (other is null) return false;
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }

    public override bool Equals(object? obj)
    {
        if (obj as Money is null) return false;
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        return Equals((Money)obj);
    }

    public override int GetHashCode()
    {
        return EqualityComparer<decimal>.Default.GetHashCode(Value);
    }

    public override string ToString()
    {
        return Value.ToString("0.00", CultureInfo.CreateSpecificCulture("en-US"));
    }
}

配置

internal class OrderConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> builder)
    {
        builder.HasKey(o => o.Id);

        builder
            .Property(o => o.Id)
            .HasConversion(id => id.Value, id => new AggregateId(id));

        builder.OwnsOne(i => i.Price, navigation =>
        {
            navigation.Property(m => m.Value).HasColumnName("Cost").IsRequired().HasPrecision(14, 4);
        });
    }
}

集成测试

public class OrdersTests : IClassFixture<TestApplicationFactory<Program>>,
    IClassFixture<TestDbContext>
{
    [Fact]
    public async Task should_get_order()
    {
        var order = Order.Create();
        await _dbContext.AddAsync(order); // here there are some exceptions
        await _dbContext.SaveChangesAsync();
        
        var response = await _client.Request($"{Path}/{order.Id.Value}").GetAsync();
        var order = await response.GetJsonAsync<Order>();

        order.ShouldNotBeNull();
    }

    // some other tests
}

public class TestApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseEnvironment("test");
    }
}

public class TestDbContext : IDisposable
{
    internal OrderDbContext DbContext { get; }

    private const string AppSettings = "appsettings.test.json";

    private static readonly IConfiguration Configuration =  new ConfigurationBuilder().AddJsonFile(AppSettings)
                                                      .AddEnvironmentVariables()
                                                      .Build();

    public TestDbContext()
    {
        DbContext = new OrderDbContext(new DbContextOptionsBuilder<OrderDbContext>().UseNpgsql(Configuration["database:connectionString"])
                                    .EnableSensitiveDataLogging()
                                    .Options);
    }

    public void Dispose()
    {
        DbContext?.Database.EnsureDeleted();
        DbContext?.Dispose();
    }
}

当我将 Price 更改为 new Money(0) 时,没有例外。 也许问题出在公共 static 只读字段中?

信息:

System.InvalidOperationException : The property 'Order.Price#Money.OrderId' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal.

堆栈跟踪:

    InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified, Boolean isConceptualNull, Boolean acceptChanges)
ChangeDetector.PropertyChanged(InternalEntityEntry entry, IPropertyBase propertyBase, Boolean setModified)
InternalEntityEntryNotifier.PropertyChanged(InternalEntityEntry entry, IPropertyBase property, Boolean setModified)
InternalEntityEntry.SetProperty(IPropertyBase propertyBase, Object value, Boolean isMaterialization, Boolean setModified, Boolean isCascadeDelete, CurrentValueType valueType)
InternalEntityEntry.SetProperty(IPropertyBase propertyBase, Object value, Boolean isMaterialization, Boolean setModified, Boolean isCascadeDelete)
InternalEntityEntry.PropagateValue(InternalEntityEntry principalEntry, IProperty principalProperty, IProperty dependentProperty, Boolean isMaterialization, Boolean setModified)
NavigationFixer.SetForeignKeyProperties(InternalEntityEntry dependentEntry, InternalEntityEntry principalEntry, IForeignKey foreignKey, Boolean setModified, Boolean fromQuery)
NavigationFixer.InitialFixup(InternalEntityEntry entry, Boolean fromQuery)
NavigationFixer.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)
InternalEntityEntryNotifier.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)
InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
DbContext.Add[TEntity](TEntity entity)
OrdersControllerTests.given_valid_order_item_should_add_to_order() line 24
--- End of stack trace from previous location ---
问题暂未有回复.您可以查看右边的相关问题.
1 实体类型的属性是键的一部分,因此无法进行修改或将其标记为已修改。 EF核心Dotnet核心

过去曾以不同的形式询问过这个问题,但我仍然无法使其正常工作。 我正在dotnet核心/ ef核心中构建一个站点,并且在一页上我想使用jqGrid轻松编辑一个经常更改的表。 该表称为CoList(公司列表) 我已经可以添加新行,也可以删除它们,但是当我尝试编辑行时,出现此错误: ...

6 实体的深层复制导致“实体类型‘X’上的属性‘Id’是键的一部分,因此不能修改或标记为已修改

我有一对多的关系,我正在尝试复制它们。 可能有更好的深度复制方法,但我认为这会奏效。 但是,当我 addAsync 我得到错误: 实体类型“Child”的属性“Id”是键的一部分,因此不能修改或标记为已修改。 要使用标识外键更改现有实体的主体,首先删除依赖项并调用“SaveChanges”,然后将依 ...

7 实体类型“UserRole”上的属性“RoleId”是键的一部分,因此不能修改或标记为已修改

在我的 .NET Core FrameworkCore 应用程序中,我收到以下错误:实体类型“UserRole”上的属性“RoleId”是键的一部分,因此无法修改或标记为已修改。 要使用标识外键更改现有实体的主体,首先删除依赖项并调用“SaveChanges”,然后将依赖项与新主体关联。 为什么它建 ...

10 EF Core:错误无法修改 OwnsOne 属性

我正在使用 EF Core 管理 SQL Server 2016 数据库(使用代码优先方法)。 特别是,我有 2 个实体之间的关系:Events 和 ObjectsForEvents。 这些是实体: 当我尝试使用它们的对象插入许多事件时,来自第一个事件的对象被插入没有问题。 尝试为第二个事件插入对象 ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM