2017-03-01 114 views
0

所以我一直在阅读,似乎大多数问题是你必须不相交的上下文,并且解决方案似乎通常创建一些外键。然而,我的问题是在一个单一的实体上,而不是与这个实体的关系,而且我可以根据需要想到外键。我认为它与使用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中实现。好极了。

+0

相反_writer.Save(客户)的你有没有试着用db.SaveChanges();客户变更后? –

回答

0

有()

db.Customer.AddOrUpdate(customer); 

那你可以使用一个叫做AddOrUpdate的方法。这是一个扩展方法,您可以在这里了解更多信息: https://msdn.microsoft.com/en-us/library/hh846520(v=vs.103).aspx

+0

不幸的是,这在EF内核中尚未实现,但我认为现在的方法是为我创建自己的版本。 – Dennis

0

是的,如果ID已经存在或不存在,您需要先查询数据库。

0

您可能必须更改以下内容的实现:_writer.Save(customer); 它应该是这个样子:

public void Save(Customer customer) 
{ 
    var dalCustomer = context.Customers.FirstOrDefault(c => c.Id = entity.Id); 
    if (dalCustomer == null) 
    { 
     dalCustomer = new Customer(); 
     context.Customers.Add(dalCustomer); 
    } 

    dalCustomer.Name = customer.Name; 
    dalCustomer.CreatedAt = DateTime.Now; 

    context.SaveChanges(); 
} 

(此代码是没有测试或编译)