使用TransactionScope& SqlCommand使数据库保持同步

时间:2015-11-18 19:56:15

标签: c# database entity-framework

我们在具有相似(足够)架构的表中的两个数据库中跟踪相同的信息。当我们更新一个数据库中的数据时,我们希望确保数据与另一个数据库中的表保持同步。

我们在两个数据库中使用Entity Framework 5,因此我原本只想导入辅助数据库的DbContext并使用TransactionsScope来确保创建/更新是原子的。

但是,我很快发现编码会很麻烦,因为表名是相同的(在这个控制器中工作的任何人都必须将Product表称为<Conext>.Product),所以我在辅助表中使用了SqlConnection个对象,但收到了一些我不太理解的结果。

如果我使用下面的语法,这两个表将原子更新/一切按计划进行。

var scopeOptions = new TransactionOptions();
scopeOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
scopeOptions.Timeout = TimeSpan.MaxValue;

var sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Monet"].ConnectionString);
sqlConn.Open();
SqlCommand sqlCommand = sqlConn.CreateCommand();
sqlCommand.CommandText = InsertMonetProduct(product);

using (var ts = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
    db.Product.Add(product);
    db.SaveChanges(); 
    sqlCommand.ExecuteNonQuery();
    ts.Complete();
}

但是,如果我在下面使用此语法,代码会在db.SaveChanges()命令崩溃,并显示以下消息:

  

已禁用分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具在MSDTC的安全配置中启用DTC以进行网络访问。

var scopeOptions = new TransactionOptions();
scopeOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
scopeOptions.Timeout = TimeSpan.MaxValue;

using (var ts = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
    using(var sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Monet"].ConnectionString))
    {
        sqlConn.Open();
        using (SqlCommand sqlCommand = sqlConn.CreateCommand())
        {
            sqlCommand.CommandText = InsertMonetProduct(product);
            sqlCommand.ExecuteNonQuery();

            db.Product.Add(product);
            db.SaveChanges();                                
        }
        ts.Complete();
    }
}

知道为什么第一个语法有效,第二个崩溃?根据我所读的online,这应该是对数据库/数据库服务器本身所做的更改。

1 个答案:

答案 0 :(得分:0)

第二位代码导致错误,因为它在单个TransactionScope内打开多个数据库连接。当程序在单个作用域内打开第二个数据库连接时,它将被提升为分布式事务。 You can read more information about distributed transactions here

在一个事务范围内搜索&#34;多个数据库连接&#34;将帮助您找到更多StackOverflow帖子。这是两个相关的:

在您走进分布式交易的土地之前,对于这种情况可能有一个更简单的解决方案。事务范围可以嵌套,如果任何嵌套范围失败,父范围将回滚。每个范围只需要担心一个连接或只是嵌套范围,因此我们可能不会遇到MSDTC问题。

尝试一下:

var scopeOptions = new TransactionOptions();
scopeOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
scopeOptions.Timeout = TimeSpan.MaxValue;

using (var ts = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
    using (var scope1 = new TransactionScope(TransactionScopeOption.Required))
    {
        // if you can wrap a using statment around the db context here that would be good
        db.Product.Add(product);
        db.SaveChanges();
        scope1.Complete();
    }
    using (var scope2 = new TransactionScope(TransactionScopeOption.Required))
    {
        // omitted the other "using" statments for the connection/command part for brevity
        var sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Monet"].ConnectionString);
        sqlConn.Open();
        SqlCommand sqlCommand = sqlConn.CreateCommand();
        sqlCommand.CommandText = InsertMonetProduct(product);
        sqlCommand.ExecuteNonQuery(); // if this fails, the parent scope will roll everything back
        scope2.Complete();
    }
    ts.Complete();
}