EF 5.0不会使用计算的标识值回滚事务

时间:2013-03-27 03:01:24

标签: c# entity-framework entity-framework-5 transactionscope auto-generate

我有两张桌子,HOTEL和OWNER。 两者都有和Identity列,但是其中一个具有另一个表所需的外键。

我需要同时以事务方式添加两个记录,但如果主表上的插入失败,我需要回滚写入辅助表记录的事务。

据我所知,我需要.SaveChanges()从辅助表中获取自动生成的ID,但这似乎也是在提交事务。

还有其他办法吗?

public class HOTEL
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Int64 HOTEL_ID { get; set; }

    public string blah1 { get; set; }

    public string blah2 { get; set; }
}

public class OWNER
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Int64 OWNER_ID { get; set; }

    public string blah3 { get; set; }

    public string blah4 { get; set; }

    public Int64 HOTEL_ID { get; set; }

    [ForeignKey("HOTEL_ID")]
    public virtual HOTEL HOTEL { get; set; }
}

...

public class MyContext : DbContext
{
    public MyContext() : base() { }
    public MyContext(string connectionString) : base(connectionString) { }

    public DbSet<HOTEL> HOTELs { get; set; }
    public DbSet<OWNER> OWNERs { get; set; }

    public ObjectContext ObjectContext
    {
        get
        {
            return (this as IObjectContextAdapter).ObjectContext;
        }
    }
}

...

Int64 ret = 0;

// Suppress required for DB2.
using (var transaction = new TransactionScope(TransactionScopeOption.Suppress))  
{
    try
    {
        using (var context = new MyContext())
        {
            var secondaryEntity = new HOTEL();
            context.HOTELs.Add(secondaryEntity);

            // This appears to commit the changes in the trasaction.
            context.SaveChanges();

            primaryEntity.HOTEL_ID = secondaryEntity.HOTEL_ID;

            context.OWNERs.Attach(primaryEntity);
            context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified;

            context.SaveChanges();
            ret = primaryEntity.OWNER_ID;
        }
    }
    catch (Exception ex)
    {
        // Deal with errors.
    }

    if (ret != 0)
    {
        transaction.Complete();
    }
}

return ret;

1 个答案:

答案 0 :(得分:0)

在完成这个工作之后,事实证明真正的问题是因为我使用的是TransactionScopeOption.Suppress。 我正在使用它,因为看起来DB2没有使用任何其他选项。这是因为我使用的是Client 9.7 Fixpack 4。 使用任何其他TransactionScopeOption时,它返回以下错误。

ERROR [57016] [IBM][DB2/NT64] SQL0668N  Operation not allowed for reason code "7" on table "DB2ADMIN.HOTEL".

深入研究,这是因为DB2没有参与TransactionScope。 在查看了这个问题上的其他一些问题之后,通过以下步骤解决了这个问题。

  • 在MSDTC属性中启用XA事务
    1. 启动,运行“dcomcnfg”以启动组件服务管理控制台。
    2. 将树导航到“组件服务&gt;电脑&gt;我的电脑&gt;分布式事务处理协调器&gt;本地DTC“。
    3. 在“本地DTC”上,右键单击并选择“属性”。
    4. 选择“安全”标签。
    5. 选中“启用XA交易”标签。
    6. 按OK。
  • 如果我继续使用DB2 9.7 FP4,我需要在注册表项HKLM \ SOFTWARE \ Microsoft \ MSDTC \ XADLL下输入一个名为“DB2CLI.DLL”且值为“C:\ Program Files”的值IBM \ SQLLIB \ BIN \ DB2CLI.DLL'

    [HKEY_LOCAL_MACHINE \ SOFTWARE \微软\ MSDTC \ XADLL] “DB2CLI.DLL”=“C:\ Program Files \ IBM \ SQLLIB \ BIN \ db2cli.dll”

  • 或者(从注册表项中),至少安装DB2 Client 9.7 FP6。

  • 重新启动计算机。
  • 在处理TransactionScope之前,请勿关闭连接。

最终的代码就像这样。

Int64 ret = 0;

using (var transaction = new TransactionScope(TransactionScopeOption.Required))
{
    try
    {
        using (var context = new MyContext())
        {
            var secondaryEntity = new HOTEL();
            context.HOTELs.Add(secondaryEntity);

            // SaveChanges(bool) has been depricated, use SaveOptions.
            // SaveChanges is required to generate the autogenerated Identity HOTEL_ID.
            context.ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
            primaryEntity.HOTEL_ID = secondaryEntity.HOTEL_ID;

            context.OWNERs.Attach(primaryEntity);
            context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified;

            context.ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
            ret = primaryEntity.OWNER_ID;

            if (ret != 0)
            {
                transaction.Complete();
                context.ObjectContext.AcceptAllChanges();
            }
        }
    }
    catch (Exception ex)
    {
        // Deal with errors.
    }
}

return ret;

从@Pawel读取注释后的替代方法,它将生成secondaryEntity的ID并自动将其附加到primaryEntity,而无需手动分配值:

Int64 ret = 0;

using (var transaction = new TransactionScope(TransactionScopeOption.Required))
{
    try
    {
        using (var context = new MyContext())
        {
            var secondaryEntity = new HOTEL();
            primaryEntity.HOTEL = secondaryEntity;

            context.HOTELs.Add(secondaryEntity);
            context.OWNERs.Attach(primaryEntity);
            context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified;

            context.SaveChanges();
            ret = primaryEntity.OWNER_ID;

            // TransactionScopeOption.Required can still used in case something 
            // goes wrong with additional processing at this point.

            if (ret != 0)
            {
                transaction.Complete();
            }
        }
    }
    catch (Exception ex)
    {
        // Deal with errors.
    }
}

return ret;