所以我一直在阅读,似乎大多数问题是你必须不相交的上下文,并且解决方案似乎通常创建一些外键。然而,我的问题是在一个单一的实体上,而不是与这个实体的关系,而且我可以根据需要想到外键。我认为它与使用GUID作为主键有关。实体框架核心保存单个实体而不是更新
我犯罪的实体是:
public class Customer
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<Organization> Organizations { get; set; }
}
已配置了一口流利的API如下:
public override void Configure(EntityTypeBuilder<Customer> entity)
{
entity.HasKey(customer => customer.Id); // Primary key
entity.Property(customer => customer.Name).IsRequired().HasMaxLength(255);
entity.Property(customer => customer.CreatedAt).IsRequired();
entity.HasMany(customer => customer.Organizations)
.WithOne(organization => organization.Customer)
.HasForeignKey(organization => organization.CustomerId);
}
生成下面的SQL:
CREATE TABLE [Customer] (
[Id] uniqueidentifier NOT NULL,
[CreatedAt] datetime2 NOT NULL,
[Name] nvarchar(255) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY ([Id])
);
我救它与此代码:
public class AddCustomer : ICommand
{
public Guid CustomerId { get; set; }
public string Name { get; set; }
}
[Transactional]
internal sealed class HandleAddCustomer : IHandleCommand<AddCustomer>
{
private readonly IEntityWriter<Customer> _writer;
public HandleAddCustomer(IEntityWriter<Customer> writer)
{
_writer = writer;
}
public Task HandleAsync(AddCustomer command)
{
var customer = new Customer
{
Id = command.CustomerId,
Name = command.Name,
CreatedAt = DateTime.Now
};
_writer.Save(customer);
return Task.CompletedTask;
}
}
entityWriter是我们为更加均匀地处理数据库交互而创建的系统,它是一个支持实体框架的包装器,并用SimpleInjector和DI包装它。
参数命令populatedfrom的API端点:
[HttpPost("AddCustomer")]
public async Task<IActionResult> AddCustomer(AddCustomer customer)
{
await _commands.ExecuteAsync(customer);
return Ok("Customer Saved");
}
通过以下有效载荷:
{
"CustomerId": "692da47a-3886-42bb-a1ac-d853d315109a",
"Name": "Test Customer"
}
所有工作正常,花花公子,直到我发送相同的ID。
{
"CustomerId": "692da47a-3886-42bb-a1ac-d853d315109a",
"Name": "New name"
}
然后我得到了一个外键冲突,因为它试图将它插入新行,从而导致过程的重复的主键。所以我认为,我的罪魁祸首是处理Id。我见过的人建议
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
但不是由数据库(来自现有的脱节系统)产生我的主键,所以我只是希望它坚持主键,看看它的同一个实体它必须更新它。我需要一些
if(_queries<Customer>().Where(c => c.Id == command.Id).FirstOrDefault() != null) { ...//The customer already exists, update the name in the entity and save it again
它只是感觉有点意大利面条codeish为现有的实体每次我执行命令时手动检查首先查询数据库,但尽管我与EF的经验是有限的(尤其是EF Core),它可能只是我对它的期望太高了。
编辑:我已经挖通过过去的员工写了一些代码,它似乎是EF整合(尤其是保存)只设置实体的实际状态:
public void Save(TEntity entity)
{
var context = _contextProvider();
var entry = context.Entry(entity);
// If it is not tracked by the context, add it to the context
if (entry.State == EntityState.Detached)
{
// This also sets the entity state to added.
context.Set<TEntity>().Add(entity);
}
else
{
// Tells the context that the entity should be updated during saving changes
entry.State = EntityState.Modified;
}
}
而且然后工作单元处理与SaveChangesAsync():
public async Task ExecuteAsync()
{
await _contextProvider().SaveChangesAsync().ConfigureAwait(false);
}
但随着关于弗雷德里克的建议,我可能需要更新图书馆也使用AddOrUpdate()偷偷摸摸编辑这似乎还没有在EF Core中实现。好极了。
相反_writer.Save(客户)的你有没有试着用db.SaveChanges();客户变更后? –