2015-07-19 81 views
0

比方说,我有两个表,AB,其中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.id后:B.id得到由数据库作为一个递增的主键分配。

这是想我写的,但不能:

b = new B { }; 
a = new A { b = b }; 
db.Add(b); 
db.Add(a); 
db.SaveChanges() 

我怎样才能做到这一点使用实体框架?

谢谢!

+1

TableB中的ID列是它的标识列吗? –

+1

是的,'A.id'和'B.id'都是。我会做一个额外的记录。 – liszt

+1

EF默认使用事务(http://stackoverflow.com/questions/815586/entity-framework-using-transactions-or-savechangesfalse-and-acceptallchanges),只需添加两条记录并调用SaveChanges()即可。 – CodeCaster

回答

3

这听起来像你的机型没有navigation properties,而是只有外键属性。

在实体框架中,您可以利用导航属性来允许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.Ida.BIdb.Id自动执行一个SQL事务同时插入aa.B到数据库中,并在成功时,填充。

//Add A and B to the database 

A a = new A(); 
a.B = new B(); 
db.Add(a); 
db.SaveChanges(); 

注意,具有导航性能,您还可以执行其他有用的任务,例如获取所有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 
} 
1

也许从你的代码存储过程调用,程序可以是这样的......

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 
1

从EF 6日起,微软建议使用dbContext.Database.BeginTransaction过的TransactionScope(https://msdn.microsoft.com/en-us/data/dn456843.aspx

基本上你必须在这样的使用块中包装交易:

using (var dbContextTransaction = db.Database.BeginTransaction()) 
{ 
    b = new B { }; 
    a = new A { b = b }; 
    db.Add(b); 
    db.Add(a); 
    db.SaveChanges() 

    dbContextTransaction.Commit(); 
} 

那么我建议你阅读有关工作模式的单位,因为它会帮你管理事务(http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

1

由于@Ciaes在他的评论中提到,你不需要创建一个事务来实现你需要什么。假设你有一个这样的模式:

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;} 
} 

唯一你需要做的添加两个新的实体是这样的:

var a = new A(){B=new B()}; 
db.SaveChanges(); 

两个实体将在一个事务中插入。

this MSDN页面:

如果添加的实体必须是 尚未追踪那么这些新的实体也将被添加到 背景下,将被插入到数据库中的其他实体引用在下一次 调用SaveChanges

0

我试着和它的工作: CREATE程序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 回滚事务;