我在ASP.NET中的实体框架有问题。每当我向数据库中添加一个对象时,我都想获取ID值。我该怎么做?
- 下面由Ladislav Mrnka给出的答案是正确的,因此应予以接受。
- OP只在他/她的账户上发布过一次,这个问题更具体。这使他/她获得了近2公里的声誉。,由于该帐户自该日起被放弃,因此答案未标记为已接受。
- 如果您只需要ID就可以在外键关系中使用它,那么您可以考虑将依赖实体的导航属性设置为以前添加的实体。这样你就不用费心马上打电话给SaveChanges,只是为了拿到身份证。请继续阅读。
这很容易。如果您使用的是DB生成的ID(如MS SQL中的IDENTITY,则只需在相关ObjectContext上向ObjectSet和SaveChanges添加实体即可。Id将自动为您填写:
1 2 3 4 5 6 7
| using (var context = new MyContext ())
{
context .MyEntities.AddObject(myNewObject );
context .SaveChanges();
int id = myNewObject .Id; // Yes it's here
} |
当使用自动生成的Id时,默认情况下,实体框架遵循每个INSERT和SELECT SCOPE_IDENTITY()。
- 所以你问错了问题。如果您有异常问题,您应该询问显示异常(和内部异常)的问题,以及导致该错误的代码段。
- 确保要添加的实体是有效的实体,例如,必须填写数据库限制为"非空"的任何内容等。
- @ladislavmrnka:新创建的对象的导航属性如何?保存更改后,它们似乎不会自动填充。
- 事实上,装载似乎很慢。
- 让我们考虑下面的场景:Table1应该生成一个@@Identity来在Table2中使用它。表1和表2应该在同一个事务中,例如,一个savechange操作必须保存两个表的数据。@@除非调用"savechanges",否则不会生成标识。我们如何解决这种情况?
- 外键方案将作为"MyNewObject"的属性列出。只需将所有必需的对象添加到所有必需的表中,并在保存前根据需要引用每个对象。
- 如果我添加多个新对象怎么办?例如,如果我在一个循环中执行3addobject,如何检索3个新的ID?
- 在saveChanges()之前无法获取ID,对吗?因为我需要一个所有插入对象的列表,我不想为每个对象保存。
- @djeroen:如果使用数据库生成的ID,则需要首先将这些对象插入数据库。如果在插入之前需要有ID,则必须构建自己的逻辑,以便在插入数据之前获得唯一的ID,并且不要在数据库中使用标识列。
- 我讨厌人们问问题而没有正确的答案。
- 如果我在context.SaveChanges();之后编辑myNewObject,然后再次调用context.SaveChanges();,更改会保存在数据库中吗?
在使用实体框架时,我一直在使用Ladislav Mrnka的答案来成功地检索ID,但是我在这里发布,因为我错过了使用它(即在不需要的地方使用它),并且我认为我会在这里发布我的发现,以防人们"解决"我遇到的问题。
考虑与客户具有外键关系的订单对象。当我同时添加新客户和新订单时,我正在做类似的事情;
1 2 3 4 5
| 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之间有一个外键关系。
我要做的就是这个;
1 2 3
| 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也会添加到订单中。我不需要额外的步骤
我知道这并不能回答最初的问题,但我认为这可能有助于开发人员谁是新的英孚从过度使用投票的答案,为一些可能不需要的东西。
- 谢谢!重要的最佳实践提示:var order=new order customer=customer;这显示了实体框架分配相关对象的能力,并且ID将自动更新。
保存更改后需要重新加载实体。因为它已被数据库触发器更改,而该数据库触发器不能由ef跟踪。所以我们需要从数据库重新加载实体,
1
| db.Entry(MyNewObject).GetDatabaseValues(); |
然后
1
| int id = myNewObject.Id; |
看看下面问题中的@jayantha答案:
使用DefaultValue时,如何获取实体框架中插入实体的ID?
在下面的问题中寻找@Christian的答案也会有所帮助:
实体框架刷新上下文?
- 嗨,当我这样做的时候,我得到了一个编译错误,上面写着:不能解析ID或名称等属性,你能帮我吗?
- @Mortezaazizi这似乎是一个未处理或空属性问题的错误,但是您能更详细地解释或编写一些代码片段吗?
- 如果您只是将模型保存到数据库中,那么就不必执行.GetDatabaseValues();。ID应该自动重新填充到模型中。
- 您绝对可以获取标识或其他数据库生成的标识符的ID。作为SQL语句的一部分,SELECT @@identity将返回最后插入列的ID。
- @克里尔加你说得对,但这是关于用ef添加一个新的对象(记录),而不是纯tsql。要获取@@Identity值,需要使用存储过程或使用sqlquery函数运行原始SQL。否则,如果您插入一个来自ef的新记录,您将如何获得@@identity值?
- @我没有尝试过从EF6,但在EF5,我看到重新填充模型在很多情况下没有发生。
- @qmaster,除了ef还能够(并且能够)执行非常相同的SQL语句并填充对象的ID。否则,它如何能够同时保存多个依赖对象?如果它不知道要在数据库中查找的对象的ID,它将如何能够执行GetDatabaseValues()?
- @Krillgar我不是ef的专家,我也没有尝试检查ef源以找到跟踪更改的策略,因为你问ef如何在不知道ID的情况下获取databaseValues()?可能它使用另一个临时ID或另一个解决方案进行跟踪。请注意,我将Ladislav Mrnka的回答上调为可接受的答案,我想描述一下我之前面对的一些场景的解决方案。我不能把答案作为可以理解的评论发表,所以我决定把它作为另一个答案发表。当我们在某些情况下无法获取ID时,您能解释一下getDatabaseValues()有什么问题吗?
- @Q马斯特,因为我主要和EF6一起工作,所以我不能和EF5说话。.GetDatabaseValues()可能没问题,显然它对你有用。只是想说,至少对于EF6来说,它确实会自动地重新填充到模型中,使得现在不再需要它,至少对于EF6来说,使它只是不必要的代码,就这样。
- @我看到瓦普盖伊了。谢谢你的关心。我会尽快在两个版本的ef中检查这一点。
- @vapcguy fwiw,模型在ef core 2.2.x中自动重新填充。
请参考此链接。
http://www.ladislavmrnka.com/2011/03/the-bug-in-storegeneratedpattern-fixed-in-vs-2010-sp1/
您必须将StoreGeneratedPattern的属性设置为Identity,然后尝试自己的代码。
或者你也可以用这个。
1 2 3 4 5 6 7
| using (var context = new MyContext ())
{
context .MyEntities.AddObject(myNewObject );
context .SaveChanges();
int id = myNewObject .Id; // Your Identity column ID
} |
- 与最热门的答案相同。
- 埃多克斯一〔3〕是我丢失的那块。
- 这就是在使用Oracle和Insert触发器时要做的,
将更改传播到数据库后,要保存的对象应该具有正确的Id。
1 2 3
| Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id; |
这行得通。
- 存储库不负责将更改保存到数据库中。这是工作的一部分。
- 此外,还不清楚Repository是什么。"这行得通"是个解释!.
您只能在保存后获取ID,而可以在保存前创建新的GUID和分配。
我遇到了一种情况,需要在数据库中插入数据,同时需要使用实体框架的主ID。解决方案:
1 2 3 4 5 6 7 8 9 10
| long id ;
IGenericQueryRepository <myentityclass, Entityname > InfoBase = null;
try
{
InfoBase = new GenericQueryRepository <myentityclass, Entityname >();
InfoBase .Add(generalinfo );
InfoBase .Context.SaveChanges();
id = entityclassobj .ID;
return id ;
} |
所有答案都非常适合他们自己的场景,我做的不同之处在于,我直接从对象(tenty)分配了int pk,该对象(tenty)的add()返回到这样的int变量;
1 2 3 4 5 6 7 8 9 10 11 12
| 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赋值。
- @Gertarnold我不能说或者说任何事情都会神奇地发生,我从添加返回的对象中得到了我想要的东西?是我做错了什么吗?如果我错了,请详细说明,否则我认为你的陈述是被动的。
- 如果将实体添加到dbset,则不会更改其ID。EmployeeId为0。
- 它确实为我提供/返回了新创建的记录的ID(从不为0或空),而且要清楚,这个代码段在我的项目中工作得很好。所以我认为这符合我的目的。
- 没有SaveChanges。不可能的。除非您有其他分配EmployeeId的进程,例如在Employee的构造函数中。或者你在EF核心使用ForSqlServerUseSequenceHiLo。不管怎么说,你不会给出整个画面。
- @我为你编辑了答案,你自己看,是的,我没有保存更改,没有EF核心!
- 我最后一句话是:这不是标准的英孚行为。您应该提供环境的更多细节。EF版本、数据库品牌和版本、员工类及其映射详细信息。
有两种策略:
使用数据库生成的Id(int或GUID)
欺骗:
您应该执行SaveChanges()以获取刚刚保存实体的Id。
赞成的意见:
可以使用int身份。
仅使用客户端生成的Id-guid。
赞成的意见:SaveChanges操作的缩小。能够在每次操作中插入新对象的大图表。
欺骗:
仅允许用于GUID。
当您首先使用EF6.x代码时
1 2
| [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; } |
并初始化数据库表,它将
在表头默认值或绑定下的表属性中,允许在插入时填充ID。
问题是,如果创建一个表并添加
1
| [DatabaseGenerated(DatabaseGeneratedOption.Identity)] |
稍后,将来的更新数据库将不会添加回(newSequentialID())
正确的方法是清除迁移,删除数据库,然后重新迁移…或者只需将(newSequentialID())添加到表设计器中。