如何在实体框架代码优先数据库中手动设置实体主键?

Posted

技术标签:

【中文标题】如何在实体框架代码优先数据库中手动设置实体主键?【英文标题】:How to manually set entity primary key in Entity Framework code first database? 【发布时间】:2014-04-11 06:50:39 【问题描述】:

好吧,我有以下模型结构:我有一个类 - DatabaseEntity 基本上是

public class DatabaseEntity

    public int Id  get; set; 

因此每个实体(如产品、类别等)都将继承 DatabaseEntity 并具有 Id 属性。我也有典型的EntityFramework 存储库类和InsertOrUpdate 方法:

private readonly DbContext _database;

public void InsertOrUpdate<TObject>(TObject entity) where TObject : DatabaseEntity

    if(entity.Id == default(int))
    
         // New entity
         DbSet<TObject>().Add(entity);
    
    else
    
         // Existing entity
         _database.Entry(entity).State = EntityState.Modified;
    
    _database.SaveChanges();

然后我通过 eBay api 从 eBay 下载我必须添加到数据库的类别列表。基本上类别是:

public class EbayCategory : DatabaseEntity

    // It has Id since it inherits DatabaseEntity
    public string Name  get; set;       
    // ... some other properties

但是,问题是,当我下载我下载的那些类别及其 Id 属性时,它们当然已经具有值。当我尝试将它们保存到数据库时:

public void UpdateCategories(IEnumerable<EbayCategory> newCategories)

    foreach (var newCategory in newCategories)
    
        _repository.InsertOrUpdate(newCategory);
    

我面临一些问题...首先,entity.Id != default(int) 因为它具有价值,所以存储库尝试更新此实体,而不是添加,但它不在数据库或上下文中,因此它抛出以下异常:

System.Data.Entity.Infrastructure.DbUpdateConcurencyException
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."

... 因为它认为其他人删除了我正在尝试更新的实体。我如何保存这个 InsertOrUpdate 逻辑,因为很多项目都基于它,并且能够将带有主键 (Id) 的项目 (EbayCategories) 添加到数据库,然后像其他实体一样更新/删除它们而不丢弃EbayCategory.Id值?

【问题讨论】:

您需要Id值吗?为什么不丢弃它,让 sql server 通过 Identity 生成一个新的 Id? @RosdiKasim,是的,我需要那个 Id 值。 你其他班级的Id情况如何? 我看不出有任何方法可以在不修改 InsertOrUpdate 方法的情况下做到这一点。 您可以更改您的方法以首先检查数据库,如按 id 查询,如果项目存在,则更新,否则插入。但是,如果有很多物品,这可能会变得很贵。 【参考方案1】:

要允许您手动生成 ID,您需要一个具有手动生成 ID 的类 - 因此它不能从 DatabaseEntity 继承

public class EbayCategory

    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id  get; set; 

    public string Name  get; set;       
    // ... some other properties

现在您将需要一个不同的InsertOrUpdate 来处理具有手动生成密钥的实体:

public void InsertOrUpdate(EbayCategory entity)

    if(Find(entity.ID == null)
    
         // New entity
         DbSet<EbayCategory>().Add(entity);
    
    else
    
         // Existing entity
         _database.Entry(entity).State = EntityState.Modified;
    
    _database.SaveChanges();

【讨论】:

【参考方案2】:

上面科林的回答非常正确地显示了如何使用数据注释来实现此设置。 但是在提出的问题中,实体是一个子类,因此您无法在不更改实体类的情况下添加注释。

还有另一种配置方法:Fluent Configuration。这是我使用 EntityTypeConfiguration 类的示例:

public class LookupSchoolsConfiguration : EntityTypeConfiguration<LookupSchools>
    
        public LookupSchoolsConfiguration()
        
            Property(l => l.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        
    

您也可以按照这篇文章直接向模型构建器添加配置:https://***.com/a/4999894/486028

【讨论】:

以上是关于如何在实体框架代码优先数据库中手动设置实体主键?的主要内容,如果未能解决你的问题,请参考以下文章

实体框架代码优先 - 通过主键将子实体添加到父实体

实体框架代码优先:共享主键

在代码优先实体框架中手动编辑数据库

如何在实体框架中使用字符串属性作为主键

实体框架代码优先主键约束名称

实体框架代码优先导航问题