
[英]The property on entity type is part of a key and so cannot be modified or marked as modified. EF Core Dotnet Core
[英]EF Core DDD ValueObject property is part of a key and so cannot be modified or marked as modified
您好,我有时在保存实体时遇到并行集成测试问题。 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 ---
本文暂无回复,试试以下方法:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.