如何使用实体框架检索插入实体的 ID? [关闭]

Posted

技术标签:

【中文标题】如何使用实体框架检索插入实体的 ID? [关闭]【英文标题】:How can I retrieve Id of inserted entity using Entity framework? [closed] 【发布时间】:2011-07-09 22:10:38 【问题描述】:

我在 ASP.NET 中遇到了实体框架的问题。每当我将对象添加到数据库时,我都想获取 Id 值。我该怎么做?

根据Entity Framework的解决方法是:

using (var context = new EntityContext())

    var customer = new Customer()
    
        Name = "John"
    ;

    context.Customers.Add(customer);
    context.SaveChanges();
        
    int id = customer.CustomerID;

这并没有获取数据库表身份,而是获取实体的分配ID,如果我们从表中删除一条记录,种子身份将与实体ID不匹配。

【问题讨论】:

如果您只需要 Id 以便在外键关系中使用它,您可以考虑将依赖实体的导航属性设置为先前添加的实体。这样您就无需费心立即致电SaveChanges 来获取ID。进一步阅读here. 对于 Entity Framework Core 3.0,将 acceptAllChangesOnSuccess 参数添加为 true :await _context.SaveChangesAsync(true); 【参考方案1】:

这很容易。如果您使用 DB 生成的 Id(如 MS SQL 中的 IDENTITY),您只需将实体添加到相关的 ObjectContext 上的 ObjectSetSaveChangesId 将自动为您填写:

using (var context = new MyContext())

  context.MyEntities.Add(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Yes it's here

当使用自动生成的Ids 时,实体框架默认遵循每个INSERTSELECT SCOPE_IDENTITY()

【讨论】:

所以你问错了问题。如果您遇到异常问题,您应该提出问题,显示您的异常(和内部异常)和导致该错误的代码 sn-p。 确保您添加的实体是有效实体,例如,必须填写任何具有“非空”数据库限制的内容等。 @LadislavMrnka:新创建的对象的导航属性怎么样?保存更改后,它们似乎没有自动填充。 让我们考虑以下场景:table1 应该生成一个@@identity 以在 table2 中使用它。 table1 和 table2 都应该在同一个事务中,例如一个 SaveChange 操作必须保存两个表的数据。除非调用“SaveChanges”,否则不会生成@@identity。我们该如何解决这种情况? @Djeroen:如果您使用数据库生成的 Id,则需要先将这些对象插入数据库。如果您需要在插入之前拥有 Id,则必须构建自己的逻辑以在插入数据之前获取唯一 Id,并且不要在数据库中使用标识列。【参考方案2】:

我一直在使用 Ladislav Mrnka's answer 在使用实体框架时成功检索 Id,但是我在这里发帖是因为我错过了使用它(即在不需要的地方使用它)并认为我会发布我的发现在这里以防万一人们希望“解决”我遇到的问题。

考虑一个与客户有外键关系的订单对象。当我同时添加一个新客户和一个新订单时,我正在做这样的事情;

var customer = new Customer(); //no Id yet;
var order = new Order(); //requires Customer.Id to link it to customer;
context.Customers.Add(customer);
context.SaveChanges();//this generates the Id for customer
order.CustomerId = customer.Id;//finally I can set the Id

但在我的情况下,这不是必需的,因为我在 customer.Id 和 order.CustomerId 之间有外键关系

我所要做的就是这个;

var customer = new Customer(); //no Id yet;
var order = new OrderCustomer = customer; 
context.Orders.Add(order);
context.SaveChanges();//adds customer.Id to customer and the correct CustomerId to order

现在,当我保存更改时,为客户生成的 ID 也会添加到订单中。我不需要额外的步骤

我知道这并没有回答最初的问题,但我认为它可能会帮助刚接触 EF 的开发人员避免过度使用投票最多的答案来解决可能不需要的问题。

这也意味着更新在单个事务中完成,可能会避免孤立数据(所有更新都完成,或者没有完成)。

【讨论】:

谢谢!重要的最佳实践提示:var order = new OrderCustomer = customer;这显示了 Entity Framework 分配相关对象的能力,并且 Id 将自动更新。 感谢您提供此答案的附加信息。在使用 EF 时对节省数据库往返非常有帮助。 非常感谢您。这正是我一直在寻找的。我只是相信有比两次调用“SaveChanges”更好的方法 请在您的回答中(最好以粗体字)提及这可以解决交易行为。如果其中一个对象添加失败,则部分事务将不会成功。例如。添加人员和学生。人成功,学生失败。现在人是多余的。不过感谢您提供这个出色的解决方案! @JohnnyJP 它应该可以工作到你想要的任何级别,这绝对是 EF 可以做到的。如果它不起作用,那么我的猜测是您的 EF 模型和上下文的某些方面设置不正确。也许缺少外键?【参考方案3】:

您需要在保存更改后重新加载实体。因为它已被 EF 无法跟踪的数据库触发器更改。所以我们需要从数据库中重新加载实体,

db.Entry(MyNewObject).GetDatabaseValues();

然后

int id = myNewObject.Id;

请看@jayantha 在以下问题中的回答:

How can I get Id of the inserted entity in Entity framework when using defaultValue?

在下面的问题中寻找@christian 的答案也可能会有所帮助:

Entity Framework Refresh context?

【讨论】:

嗨,当我这样做时,我收到编译错误,上面写着:无法解析属性,例如 Id 或 Name,你能帮帮我吗? 如果您刚刚将模型保存到数据库,则不必执行 .GetDatabaseValues();。 ID 应该会自动重新填充到模型中。 @QMaster 除了 EF 还能够(并且确实)执行相同的 SQL 语句并填充对象的 ID。不然怎么能同时保存多个依赖对象呢?如果它不知道要在数据库中查找的对象的 ID,它如何能够GetDatabaseValues() @vapcguy 我明白了。感谢您的关注。我会尽快检查这两个版本的 EF。 @vapcguy FWIW,模型在 EF Core 2.2.x 中自动重新填充。【参考方案4】:

您必须将StoreGeneratedPattern 的属性设置为identity,然后尝试您自己的代码。

否则你也可以使用它。

using (var context = new MyContext())

  context.MyEntities.AddObject(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Your Identity column ID

【讨论】:

与投票最多的答案相同。 StoreGeneratedPattern 对我来说是缺失的部分。 这就是使用 ORACLE 和 ON-Insert 触发器时要走的路, 链接已损坏 - 停放域 您好 Oracle 我必须在实体中设置 StoreGeneratedPattern - 转到模型查看器,找到您的表和 PK,选择 StoreGeneratedPattern 属性并设置为身份。它的工作原理如上述答案所示。这是针对您有一个触发器从插入序列中填充您的 PK 的情况。【参考方案5】:

您保存的对象在将更改传播到数据库后应该有一个正确的Id

【讨论】:

你看到内部异常了吗? ;-)【参考方案6】:

我遇到了一种情况,我需要在数据库中插入数据并同时需要使用实体框架的主 ID。 解决方案:

long id;
IGenericQueryRepository<myentityclass, Entityname> InfoBase = null;
try
 
    InfoBase = new GenericQueryRepository<myentityclass, Entityname>();
    InfoBase.Add(generalinfo);
    InfoBase.Context.SaveChanges();
    id = entityclassobj.ID;
    return id;
 

【讨论】:

【参考方案7】:

保存后才能获取ID,保存前可以新建Guid并分配。

【讨论】:

Ahmet 无论如何都想在保存后获取 ID。虽然在某些情况下是一个不错的选择,但对于这种情况来说,它是一种矫枉过正的选择。【参考方案8】:

所有答案都非常适合他们自己的场景,我所做的不同之处在于我直接从 Add() 返回的对象 (TEntity) 分配了 int PK;

using (Entities entities = new Entities())

      int employeeId = entities.Employee.Add(new Employee
                        
                            EmployeeName = employeeComplexModel.EmployeeName,
                            EmployeeCreatedDate = DateTime.Now,
                            EmployeeUpdatedDate = DateTime.Now,
                            EmployeeStatus = true
                        ).EmployeeId;

      //...use id for other work

因此,您无需创建一个全新的对象,而是获取您想要的内容 :)

为@GertArnold 先生编辑:

【讨论】:

添加一个未公开的方法来分配一个Id并不是真的有用。这甚至与实体框架无关。 @GertArnold .. 我和 OP 有同样的问题,我找到了答案并将其写成单行语句,我从未听说过 非公开方法愿意解释吗? 这只是意味着您不会展示(披露)您所做的一切。可能只是没有描述清楚,我不知道。我所知道的是Add(如果这是DbSet.Add)确实神奇地为EmployeeId赋值。 不是没有SaveChanges。不可能的。除非您有其他分配EmployeeId 的进程,例如在Employee 的构造函数中。或者您在使用 ForSqlServerUseSequenceHiLo 的 EF 核心中。无论如何,你并没有给出全貌。 我的最后一句话是:这不是标准的 EF 行为。您应该提供有关您的环境的更多详细信息。 EF 版本、数据库品牌和版本、Employee 类、其映射细节。【参考方案9】:
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

这会起作用。

【讨论】:

存储库不负责将更改保存到数据库中。这是 UnitOfWork 的工作。 此外,绝对不清楚Repository 是什么。而“这将起作用”是一些解释!。【参考方案10】:

有两种策略:

    使用数据库生成的IDintGUID

    缺点:

    您应该执行SaveChanges() 以获取刚刚保存的实体的ID

    优点:

    可以使用int身份。

    使用客户端生成的ID - 仅限 GUID。

    优点: 缩小SaveChanges 操作。 能够在每个操作中插入新对象的大图。

    缺点:

    只允许GUID

【讨论】:

【参考方案11】:

当你首先使用 EF 6.x 代码时

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id  get; set; 

并初始化一个数据库表,它会放一个

(newsequentialid())

在标题默认值或绑定下的表属性中,允许在插入 ID 时填充它。

问题是如果你创建一个表并添加

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

稍后部分,未来的更新数据库不会添加回 (newsequentialid())

修复正确的方法是擦除迁移,删除数据库并重新迁移...或者您可以将 (newsequentialid()) 添加到表设计器中。

【讨论】:

你能详细说明 newsequentialid() 的去向吗?我已经手动创建了数据库模型,而不是先使用 EF 代码,并且它没有填充 id,因为它不是我的主键。很想为此找到解决方案。 @josh 假设代码首先是 Guid 类型,在 Sql Server Management Studio 中右键单击您的表,单击 Id 列,然后在列属性选项卡下,在(常规)内,您将看到默认值或绑定。如果您是手动创建 DB 表,请参考NewSequentialId 或NewId。一般用例类似于when -column- is NULL, then NEWID()

以上是关于如何使用实体框架检索插入实体的 ID? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用实体框架通过 id 删除对象

如果存在则更新行否则使用实体框架插入逻辑[关闭]

实体框架如何处理大量记录? [关闭]

使用实体框架插入时设置 Id 字段

在实体框架中批量插入后批量插入记录并获取它们的 ID

C#:更改实体框架的检索数据类型