C# 如何使用实体框架的事务?
假设我有两个表,C# 如何使用实体框架的事务?,c#,sql,sql-server,entity-framework,C#,Sql,Sql Server,Entity Framework,假设我有两个表,A和B,其中A有一个外键指向B 以下是数据库的外观: A: | B: | id (int) | b (foreign B) | id (int) ---------+-------------- | -------- 1 | 2 | 2 2 | 5 | 5 3
A
和B
,其中A
有一个外键指向B
以下是数据库的外观:
A: | B:
|
id (int) | b (foreign B) | id (int)
---------+-------------- | --------
1 | 2 | 2
2 | 5 | 5
3 | 2 |
现在,我希望将类型为a
的新对象和类型为B
的新对象插入数据库,新的a
引用新的B
。此外,它们必须都插入到同一事务中,因为我不能允许,例如,仅插入B
,但A
失败
但是,我不知道如何插入A
而不先单独插入B
,因为我只知道B
插入B
之后:B.id
被数据库分配为递增主键
这是我想写的,但不能:
b = new B { };
a = new A { b = b };
db.Add(b);
db.Add(a);
db.SaveChanges()
如何使用实体框架实现这一点
谢谢 如@CodeCaster所述,默认情况下EF使用事务。如果您想要更细粒度的控制,请将其包装在using(TransactionScope)中。看见编辑:至于为什么你发布的代码不起作用,你需要有一个从a到B的导航属性,然后当你实例化一个类型为a的对象时,你只需将所述属性的值赋给B。可能是你代码中的一个存储过程调用,一个过程可能类似于
CREATE PROCEDURE usp_InsertRow
AS
BEGIN
SET NOCOUNT ON;
DECLARE @New_ID INT;
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO TableB DEFAULT VALUES;
SET @New_ID = SCOPE_IDENTITY();
INSERT INTO TableA (B)
VALUES (@New_ID)
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT <> 0
ROLLBACK TRANSACTION;
END CATCH
END
创建过程usp\u InsertRow
作为
开始
不计数;
声明@New_ID INT;
开始尝试
开始交易;
在表B中插入默认值;
设置@New_ID=SCOPE_IDENTITY();
插入表A(B)
值(@New_ID)
提交事务;
结束尝试
开始捕捉
如果@@TRANCOUNT 0
回滚事务;
端接
结束
从EF 6开始,Microsoft建议在TransactionScope()上使用dbContext.Database.BeginTransaction
基本上,您必须将事务包装在using块中,如下所示:
using (var dbContextTransaction = db.Database.BeginTransaction())
{
b = new B { };
a = new A { b = b };
db.Add(b);
db.Add(a);
db.SaveChanges()
dbContextTransaction.Commit();
}
public class A
{
public int Id{get;set;}
[ForeignKey("B")]
public int BId{get;set;}
public virtual B B{get;set;}
}
public class B
{
public int Id{get;set;}
public virtual ICollection<A> As{get;set;}
}
然后我建议您阅读工作单元模式,因为它将帮助您管理事务(),正如@Ciaes在他的评论中提到的,您不需要创建事务来实现所需的功能。假设你有这样一个模型:
using (var dbContextTransaction = db.Database.BeginTransaction())
{
b = new B { };
a = new A { b = b };
db.Add(b);
db.Add(a);
db.SaveChanges()
dbContextTransaction.Commit();
}
public class A
{
public int Id{get;set;}
[ForeignKey("B")]
public int BId{get;set;}
public virtual B B{get;set;}
}
public class B
{
public int Id{get;set;}
public virtual ICollection<A> As{get;set;}
}
这两个实体都将插入到单个事务中
从msdn页面:
如果要添加的实体引用了
尚未跟踪,则这些新实体也将添加到
上下文,并将在下次
SaveChanges被称为
听起来您的模型没有,而是只有外键属性 在实体框架中,您可以利用导航属性,让EF为您执行关系管理的大部分后台工作。例如:
public class A {
public int Id {get;set;}
public int BId {get;set;}
public virtual B B {get;set;}
}
public class B{
public int Id {get;set;}
public virtual ICollection<A> AColl {get;set;}
}
请注意,具有导航属性还允许您执行其他有用的任务,例如获取所有A
项以及B
,例如:
var b = db.B.Find(someId).Include(b => b.AColl);
// we now have all the A records that have B FK
foreach (A a in b.AColl){
//do something
}
我试过了,它起作用了:
创建过程usp\U InsertRow
作为
开始
不计数;
声明@New_ID INT;
开始尝试
开始交易
INSERT INTO TableB DEFAULT VALUES;
SET @New_ID = SCOPE_IDENTITY();
INSERT INTO TableA (B)
VALUES (@New_ID)
COMMIT TRANSACTION;
结束尝试
开始捕捉
如果@@TRANCOUNT 0
回滚事务 表B中的ID列是标识列吗?是的,
A.ID
和B.ID
都是。我要做一个额外的说明。默认情况下,EF使用事务(),只需添加记录并调用SaveChanges()。如果B
是a
的导航属性,则a=new a();a、 b=新的b();db.SaveChanges()代码>将是单笔交易。请发布您的模型;听起来好像没有导航属性,但只有外键值。您可以同时拥有这两个属性,即您可以拥有类似于public B{get;set;}
以及public int-BId{get;set;}
INSERT INTO TableB DEFAULT VALUES;
SET @New_ID = SCOPE_IDENTITY();
INSERT INTO TableA (B)
VALUES (@New_ID)
COMMIT TRANSACTION;