Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/74.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用实体框架的事务?_C#_Sql_Sql Server_Entity Framework - Fatal编程技术网

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;