如何在实体框架中获取插入实体的ID?[关闭]


611

我在Asp.net中遇到实体框架问题。每当将对象添加到数据库时,我都想获取Id值。我怎样才能做到这一点?


16
以下Ladislav Mrnka的答案是正确的答案,因此应被接受。
CShark '16

3
OP仅使用他/她的帐户发布了一次,这个问题将更加具体。这给了他/她近2k的声誉(!),由于该帐户从那天起就被放弃,因此答案未标记为“已接受”。
MurariAlex

1
如果只需要Id以便在外键关系中使用它,则可以考虑将您的从属实体的导航属性设置为先前添加的实体。这样,您就不必SaveChanges为了获得ID而立即打电话。在这里进一步阅读。
B12Toaster

对于Entity Framework Core 3.0,将acceptAllChangesOnSuccess参数添加为true:await _context.SaveChangesAsync(true);
麦克,

Answers:


1005

这很容易。如果您正在使用DB生成的ID(如IDENTITY在MS SQL),你只需要实体添加ObjectSetSaveChanges有关ObjectContextId将自动为您填充:

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

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

默认情况下,实体框架遵循各INSERTSELECT SCOPE_IDENTITY()时自动生成的Id用于秒。


34
所以你问的是错误的问题。如果您对异常有疑问,则应该提出问题以显示异常(和内部异常)以及导致该错误的代码段。
Ladislav Mrnka'3

3
确保您要添加的实体是有效实体,例如,必须填充数据库限制为“ not null”的任何内容,等等
。–

4
@LadislavMrnka:新创建的对象的导航属性如何?保存更改后,它们似乎不会自动填充。
艾萨克·克莱曼

4
让我们考虑以下情形:table1应该生成一个@@ identity以在table2中使用它。假设table1和table2都在同一个事务中,例如,一个SaveChange操作必须保存两个表的数据。除非调用“ SaveChanges”,否则不会生成@@ identity。我们如何解决这种情况?
Arash 2014年

7
@Djeroen:如果使用数据库生成的ID,则需要首先将那些对象插入数据库。如果您需要在插入之前具有ID,则必须构建自己的逻辑以在插入数据之前获取唯一的ID,并且不要在数据库中使用标识列。
拉迪斯拉夫·姆恩卡

123

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

考虑与客户具有外键关系的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

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

我要做的就是这个。

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

现在,当我保存更改时,还将为客户生成的ID添加到订单中。我不需要其他步骤

我知道这并不能回答最初的问题,但我认为这可能会帮助EF新手开发人员避免过度使用票房最高的答案来解决不必要的问题。

这也意味着更新可以在单个事务中完成,从而避免使用Orphin数据(要么完成更新,要么不更新)。


8
谢谢 !重要的最佳做法提示:var order = new Order {Customer = customer}; 这显示了Entity Framework分配相关对象的功能,并且ID将自动更新。
盖伊

感谢您提供答案的其他信息。使用EF时对保存数据库往返非常有帮助。
Prasad Korhale '19

非常感谢你做的这些。这正是我想要的。我只是相信,有一种方法比两次调用“ SaveChanges”更好
乔什(Josh)

1
还请在您的答案中(最好用粗体显示)提及这可以解决交易行为。如果其中一个对象添加失败,则部分事务将不会成功。例如添加人和学生。人成功,学生失败。现在人是多余的。谢谢您提供的出色解决方案!
Imran Faruqi

32

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

db.Entry(MyNewObject).GetDatabaseValues();

然后

int id = myNewObject.Id;

看看下面问题中的@jayantha答案:

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

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

实体框架刷新上下文?


2
嗨,当我这样做时,出现编译错误,提示:无法解析Id或Name等属性,请您能帮我吗?
Morteza Azizi 2014年

1
@MortezaAzizi似乎是未处理或属性值为null的错误,但是请您进一步解释或编写一些代码片段?
QMaster 2014年

3
.GetDatabaseValues();如果您只是将模型保存到数据库中,则不必这样做。该ID应自动重新填充到模型中。
vapcguy

3
@QMaster除了EF也能够(并且确实)执行该相同的SQL语句并填充对象的ID。否则,如何能够同时保存多个从属对象?它是如何将能够GetDatabaseValues(),如果它不知道对象在数据库中查找的ID?
krillgar '18

2
@vapcguy我明白了。感谢您的注意。我将在EF ASAP的两个版本中进行检查。
QMaster

16

请参考此链接。

http://www.ladislavmrnka.com/2011/03/the-bug-in-storegenicpattern-fixed-in-vs-2010-sp1/

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

否则您也可以使用它。

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

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

7
与最高投票答案相同。
Lewis86 '18

2
StoreGeneratedPattern是我缺少的一块。
BurnsBA '18 -4-2

1
这就是使用ORACLE和ON-Insert触发器时要走的路
卡尔

链接已断开-托管域名
t.durden

使用Oracle嗨,我必须在Entity中设置StoreGeneratedPattern-转到模型查看器,找到您的表和PK,选择StoreGeneratedPattern属性并设置为Identity。它按上述答案指示工作。这是在您有一个触发器从插入序列填充PK的情况下。
罗布



4

我遇到一种情况,我需要在数据库中插入数据,同时需要使用实体框架来获取主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;
 }

4

所有答案都非常适合他们自己的情况,我所做的不同是我直接从Add()返回给这样的int变量的对象(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先生:

在此处输入图片说明


3
添加未公开的方法来分配ID并没有什么用。这甚至与实体框架都不相关。
Gert Arnold

1
@GertArnold ..我有与OP相同的问题,我找到了答案并将其写成单行语句,而且我从未听说过术语“ 非公开方法”来解释吗?
IteratioN7T

3
那只是意味着您不显示(披露)您所做的一切。也许只是没有清楚地描述,我不知道。我知道的是,Add(如果是DbSet.Add)并不能奇迹般地分配一个值EmployeeId
Gert Arnold

3
不是没有SaveChanges。不可能。除非您有其他分配过程,EmployeeId例如在Employee的构造函数中。或者您使用处于EF核心ForSqlServerUseSequenceHiLo。无论如何,您并没有给出全部图片。
格特·阿诺德

3
我的最后一句话是:这不是标准的EF行为。您应该提供有关环境的更多详细信息。EF版本,数据库品牌和版本,Employee类,其映射详细信息。
格特·阿诺德

3
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

这将起作用。


4
存储库不负责将更改保存到数据库中。这是UnitOfWork的工作。
tchelidze

5
而且,还不清楚是什么Repository。而“这将起作用”是一些解释!
Gert Arnold

2

有两种策略:

  1. 使用数据库生成的IDintGUID

    缺点:

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

    优点:

    可以使用int身份。

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

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

    缺点:

    只允许 GUID


1

第一次使用EF 6.x代码时

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

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

(newsequentialid())

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

问题是,如果您创建表并添加

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

稍后,将来的更新数据库将不会重新添加(newsequentialid())

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


您能否详细说明newsequentialid()的去向?我已经手工创建了数据库模型,没有先使用EF代码,并且由于它不是我的主键,所以没有填充id。很乐意为此找到解决方案。
乔什

1
@josh假定代码首先是Guid类型,右键单击Sql Server Management Studio中的表,单击“ Id”列,然后在“列属性”选项卡下的(“常规”)内,您将看到“默认值”或“绑定”。如果要手动创建数据库表,请参考NewSequentialIdNewId。一般用例类似于when -column- is NULL, then NEWID()
Jeff Li
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.