如何使用实体框架检索插入实体的 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
上的 ObjectSet
和 SaveChanges
。 Id
将自动为您填写:
using (var context = new MyContext())
context.MyEntities.Add(myNewObject);
context.SaveChanges();
int id = myNewObject.Id; // Yes it's here
当使用自动生成的Id
s 时,实体框架默认遵循每个INSERT
和SELECT 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】:
有两种策略:
使用数据库生成的ID
(int
或GUID
)
缺点:
您应该执行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? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章