如何使用实体框架检索插入实体的 ID?

How can I retrieve Id of inserted entity using Entity framework?

提问人:ahmet 提问时间:3/7/2011 最后编辑:TylerHahmet 更新时间:1/6/2023 访问量:653757

问:

我在 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 不匹配。

C# asp.net 实体框架

评论

5赞 Felix K. 7/23/2018
如果只需要 Id 即可在外键关系中使用它,则可以考虑仅将依赖实体的导航属性设置为先前添加的实体。这样,您就不必为了获得 ID 而立即打电话。进一步阅读 这里.SaveChanges
0赞 Mike 8/30/2019
对于 Entity Framework Core 3.0,请将 acceptAllChangesOnSuccess 参数添加为 true : await _context。SaveChangesAsync(真);

答:

19赞 Snowbear 3/7/2011 #1

将更改传播到数据库后,您保存的对象应该是正确的。Id

评论

27赞 Snowbear 3/7/2011
你见过内在的例外吗?;-)
1286赞 Ladislav Mrnka 3/7/2011 #2

这很容易。如果您使用的是数据库生成的 Id(如在 MS SQL 中),则只需在相关 . 将自动为您填写:IDENTITYObjectSetSaveChangesObjectContextId

using (var context = new MyContext())
{
  context.MyEntities.Add(myNewObject);
  context.SaveChanges();

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

默认情况下,实体框架在使用自动生成的 s 时遵循每个。INSERTSELECT SCOPE_IDENTITY()Id

评论

37赞 Ladislav Mrnka 3/7/2011
所以你问错了问题。如果遇到异常问题,则应提出问题,显示异常(和内部异常)以及导致该错误的代码片段。
4赞 fran 6/3/2011
确保您添加的实体是有效的实体,例如,必须填写数据库限制为“非空”的任何内容等。
4赞 Isaac Kleinman 11/12/2014
@LadislavMrnka:新创建的对象的导航属性如何?保存更改后,它们似乎不会自动填充。
8赞 Ladislav Mrnka 12/3/2015
@Djeroen:如果使用数据库生成的 Id,则需要先将这些对象插入到数据库中。如果需要在插入之前具有 ID,则必须构建自己的逻辑以在插入数据之前获取唯一 ID,并且不要在数据库中使用标识列。
5赞 Shadi Alnamrouti 11/21/2016
我讨厌人们提出问题而不标记任何正确答案
27赞 Azhar Mansuri 12/7/2012 #3

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

否则你也可以使用它。

using (var context = new MyContext())
{
  context.MyEntities.AddObject(myNewObject);
  context.SaveChanges();

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

评论

7赞 Lewis86 2/23/2018
与得票最高的答案相同。
3赞 BurnsBA 4/2/2018
StoreGeneratedPattern是我缺失的一块。
1赞 Karl 10/13/2018
这就是使用 ORACLE 和 ON-Insert Trigger 时要走的路,
0赞 t.durden 9/5/2019
链接已断开 - 停放域
0赞 Rob 2/11/2020
嗨,使用 Oracle,我必须在 Entity 中设置 StoreGeneratedPattern - 转到模型查看器,找到您的表和 PK,选择 StoreGeneratedPattern 属性并设置为 Identity。它的工作原理如上所述。这适用于以下情况:您有一个触发器,该触发器从插入时的序列填充 PK。
41赞 QMaster 7/20/2013 #4

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

db.Entry(MyNewObject).GetDatabaseValues();

然后

int id = myNewObject.Id;

请看下面问题@jayantha答案:

使用 defaultValue 时,如何在实体框架中获取插入实体的 ID?

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

实体框架刷新上下文?

评论

2赞 Morteza Azizi 10/17/2014
嗨,当我这样做时,我收到编译错误,上面写着:无法解析属性,例如 Id 或 Name,你能帮我吗?
3赞 vapcguy 9/11/2018
如果您只是将模型保存到数据库,则不必这样做。该 ID 应自动重新填充到模型中。.GetDatabaseValues();
3赞 krillgar 9/26/2018
@QMaster 除了 EF 还能够(并且确实)执行完全相同的 SQL 语句并填充对象的 ID。否则,它如何能够同时保存多个依赖对象?如果它不知道要在数据库中查找的对象的 ID,它怎么能做到呢?GetDatabaseValues()
2赞 QMaster 9/28/2018
@vapcguy我明白了。感谢您的关注。我将尽快在两个版本的 EF 中检查这一点。
2赞 slasky 2/22/2019
@vapcguy FWIW,模型会自动在 EF Core 2.2.x 中重新填充。
4赞 Mr Kunal 8/13/2015 #5

我遇到过这样一种情况,即我需要在数据库中插入数据,同时需要使用实体框架的主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;
 }
3赞 Saket Choubey 6/29/2016 #6
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

这将起作用。

评论

4赞 tchelidze 11/11/2017
存储库不负责将更改保存到数据库中。这是 UnitOfWork 的工作。
8赞 Gert Arnold 9/13/2018
而且,目前还不清楚是什么。而“这将起作用”是一些解释!Repository
222赞 mark_h 12/14/2016 #7

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

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

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

但是,就我而言,这不是必需的,因为我在客户之间有外键关系。ID 和顺序。客户 ID

我所要做的就是这个;

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

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

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

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

评论

16赞 LA Guy 88 7/10/2017
谢谢!重要 最佳实践提示 : var order = new Order{Customer = customer};这显示了 Entity Framework 分配相关对象的能力,并且 Id 将自动更新。
3赞 Imran Faruqi 2/6/2020
还请在您的回答中提及(最好是粗体字母)这解决了交易行为。如果其中一个对象添加失败,则部分事务将不会成功。例如,添加人员和学生。人成功,学生失败。现在人是多余的。谢谢你这个伟大的解决方案!
1赞 Vereonix 10/13/2020
小更新(对于像我这样的菜鸟来说,这是显而易见的)但是.在 之前,需要为每个对象调用 Add。SaveChanges,只有这 3 行不会将新记录添加到数据库中。
1赞 mark_h 4/21/2021
@JohnnyJP它应该可以工作到您想要的任何级别,这绝对是 EF 可以做的事情。如果它不起作用,那么我的猜测是 EF 模型和上下文的某些方面设置不正确。也许缺少外键?
1赞 mark_h 12/14/2022
@Black-Pawn-C7 调用 save 时,EF 会将您在 C# 中执行的操作转换为 SQL,因为您已将客户添加到订单中,然后将订单添加到上下文中,EF 也了解您的客户,并将执行同时添加订单和客户的 SQL 语句。
4赞 Vaduganathan Pillappan 5/3/2017 #8

只有在保存后才能获取 ID,而是可以在保存前创建新的 Guid 并分配。

评论

0赞 Orhan ODABAŞI 2/19/2021
无论如何,艾哈迈德都想在保存后获得 ID。虽然对于某些场景来说是一个不错的选择,但对于这种情况来说,这有点矫枉过正。
2赞 Jeff 1/10/2018 #9

首先使用 EF 6.x 代码时

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

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

(newsequentialid())

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

问题是,如果您创建一个表并将

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

稍后,未来的 update-databases 不会添加回 (newsequentialid())

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

评论

0赞 Josh 1/10/2020
你能详细说明一下newsequentialid()的去向吗?我已经手动创建了数据库模型,而不是首先使用 EF 代码,并且它不会填充 id,因为它不是我的主键。很想找到解决方案。
1赞 Jeff 1/21/2020
@josh 假设第一个代码是 Guid 类型,请在 Sql Server Management Studio 中右键单击表,单击“Id”列,然后在“列属性”选项卡下的“(常规)”中,将看到“默认值”或“绑定”。如果您手动创建数据库表,请参阅 NewSequentialIdNewId。一般用例类似于when -column- is NULL, then NEWID()
3赞 Vladyslav Furdak 8/12/2018 #10

有两种策略:

  1. 使用数据库生成的 ( 或IDintGUID)

    缺点:

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

    优点:

    可以使用标识。int

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

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

    缺点:

    仅允许用于GUID

3赞 IteratioN7T 8/20/2018 #11

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

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先生:

enter image description here

评论

3赞 Gert Arnold 9/13/2018
添加未公开的方法来分配 ID 并不是很有用。这甚至与实体框架无关。
1赞 IteratioN7T 9/13/2018
@GertArnold ..我有和 OP 相同的问题,我找到了答案并将其制作成一行语句,我从未听说过术语“非公开方法”来解释?
3赞 Gert Arnold 9/13/2018
这只是意味着你不会展示(披露)你所做的一切。也许只是没有清楚地描述,我不知道。我所知道的是(如果这是)不会神奇地为 .AddDbSet.AddEmployeeId
3赞 Gert Arnold 9/14/2018
并非没有.不可能的。除非你有其他一些分配 的进程,例如在 的构造函数中。或者,你在 EF Core 中使用 .无论如何,你并没有给出全貌。SaveChangesEmployeeIdEmployeeForSqlServerUseSequenceHiLo
3赞 Gert Arnold 9/14/2018
我最后要说的是:这不是标准的 EF 行为。您应该提供有关环境的更多详细信息。EF 版本、数据库品牌和版本、Employee 类及其映射详细信息。
0赞 Sandeep Nandey 1/6/2023 #12

我正在使用MySQL DB,我有一个AUTO_INCREMENT字段ID

我在 EF 中遇到了同样的问题。

我尝试了以下行,但它总是返回 0。

      await _dbContext.Order_Master.AddAsync(placeOrderModel.orderMaster);
      await _dbContext.SaveChangesAsync();

      int _orderID = (int)placeOrderModel.orderMaster.Id;

enter image description here但我意识到我的错误并纠正了它。

我犯的错误:我在 Id 字段的 orderMaster 模型中传递了 0

enter image description here

解决方案奏效:一旦我从我的orderMaster模型中删除了Id字段,它就开始工作了。

enter image description here

我知道这是一个非常愚蠢的错误,但如果有人错过了这个,就放在这里。