错误 - 使用多个数据库连接的LINQ/TransactionScope

4
我正在尝试将两个事务包装到同一个SQL Server上的不同数据库中,并遇到了一些困难。最初我在网络DTC访问方面遇到了问题,但现在我持续收到的错误是“与底层事务管理器的通信失败”。
我们在一个数据库中有一些客户配置文件,当这些配置文件过时时,我们希望将它们移动到“存档”数据库进行存储。移动只是简单地(为了幽默而使用斜体)将它们添加到归档数据库并从主/活动数据库中删除它们。我为每个数据库拥有一个DataContext。以下代码执行Add操作,然后在尝试使用第二个DataContext进行Delete操作时出现错误。我只使用LINQ工作了几个月,并搜索了过去几天的文章。我想知道我的代码是否存在问题,或者DTC是否配置正确。
我们在我的工作站和服务器上都运行在VMware上。 - 工作站是Windows 7 SP1 - 服务器是Windows和SQL Server 2008R2
“Move”程序如下:
private int MoveProfileToArchiveDB( int iProfileId )
{
    int rc = RC.UnknownError;

    // get new Archive profile object
    ProfileArchive.ProfileInfo piArchive = new ProfileArchive.ProfileInfo();

    // 'Live' DataContext
    using ( ProfileDataContext dbLive = new ProfileDataContext() )
    {
        // get Live profile
        ProfileInfo piLive = ProfileInfo.GetProfile( dbLive, iProfileId );

        // copy Live data to Archive profile object... including the id
        ProfileArchive.ProfileInfo.CopyFromLive( piLive, piArchive, true );
    }

    bool bArchiveProfileExists = ProfileArchive.ProfileInfo.ProfileExists( piArchive.id );

    // make the move a transaction... 
    using ( TransactionScope ts = new TransactionScope() )
    {
        // Add/Update to Archive db
        using ( ProfileArchiveDataContext dbArchive = new ProfileArchiveDataContext() )
        {
            // if this profile already exists in the Archive db...
            if ( bArchiveProfileExists )
            {
                // update the personal profile in Archive db
                rc = ProfileArchive.ProfileInfo.UpdateProfile( dbArchive, piArchive );
            }
            else
            {
                // add this personal profile to the archive db
                int iArchiveId = 0;
                piArchive.ArchiveDate = DateTime.Now;
                rc = ProfileArchive.ProfileInfo.AddProfile( dbArchive, piArchive, ref iArchiveId );
            }

            // if Add/Update was successful...
            if ( rc == RC.Success )
            {
                // Delete from the Live db
                using ( ProfileDataContext dbLive = new ProfileDataContext() )
                {
                    // delete the personal profile from the Profile DB
                    rc = ProfileInfo.DeleteProfileExecCmd( dbLive, iProfileId );    // *** ERROR HERE ***
                    if ( rc == RC.Success )
                    {
                        // Transaction End (completed)
                        ts.Complete();
                    }
                }
            }
        }

    }

    return rc;
}

注意事项:

  1. 我有几种不同的删除方法,它们都可以在TransactionScope之外正常工作。
  2. ProfileInfo是主要的个人资料表,在Live和Archive数据库中大致相同。

非常感谢您的帮助!非常感谢...


运行 MSDTC 的账户是否具有访问数据库的正确权限? - Kane
你的归档正在客户端逐行进行,并在两个相邻的数据库之间执行DTC?当你手中只有一把锤子时......你不必为所有事情使用EF。尝试在服务器端完成它。DELETE ... OUTPUT ... INTO targetdb... FROM sourcedb ... WHERE是一个很好的起点。SSIS作业是另一个很好的选择。甚至可以使用基于T-SQL游标的解决方案。只是不要通过客户端路由每一行...... - Remus Rusanu
@Kane - 我相信是这样的。我已经克服了与MSDTC相关的另外两个错误。-“分布式事务管理器(MSDTC)的网络访问已被禁用。请使用组件服务管理工具在MSDTC的安全配置中启用DTC以进行网络访问。”和“合作伙伴事务管理器已禁用其对远程/网络事务的支持。”现在它调用Delete方法并获得我发布的错误-“与底层事务管理器的通信失败。” - TravelDev
@Remus - 另一个人告诉我,LINQ 有其适用的场景,但并非万能。我不是很擅长 SQL 脚本编写,但如果那更合理,那就这么做吧。昨天我已经倾向于这样做了,如果这是最好的选择,我可能会这样做。如果存在限制、错误等问题,我希望从中学习。根据我所读的,这个程序“应该”可以正常工作。 - TravelDev
@TravelDev:它可以工作。但是它的性能将会比它本来应该有的低上一到两个数量级。而且你还需要解决像DTC协调这样的问题,而本来你可以编写一些有用的代码。 - Remus Rusanu
@RemusRusanu - 好的。所以你确信代码应该可以工作,这是一个DTC问题?而且,你也同意在处理大量数据时最好不要让LINQ来处理吗? - TravelDev
1个回答

8

我决定将这个回答发在这里,而不是继续点对点地讨论。

  • 不要使用错误码。那是异常处理的工作。代码流程会更难读,错误码的返回结果也容易被忽略。异常处理可以使代码更易于阅读,并且更少出现错误。

  • 如果使用TransactionScope,请始终明确设置隔离级别。请参考使用新的TransactionScope()会带来危害。SERIALIZABLE的隐式隔离级别几乎从不被调用,并且对性能有巨大的负面影响。

  • 事务升级。每当在事务范围内打开多个连接时,它们可能会将事务升级为分布式事务。行为因版本而异,有些人已经试图记录它,例如TransactionScope:事务升级行为

SQL Server 2008比SQL Server 2005更加智能,可以自动检测某个事务中的所有数据库连接是否指向同一个物理数据库。如果是这种情况,则该事务仍然保持为本地事务,而不会升级为分布式事务。不幸的是,有一些注意事项:

  • 如果打开的数据库连接是嵌套的,则事务仍会升级为分布式事务。
  • 如果在事务中连接到另一个持久资源,则事务将立即升级为分布式事务。

由于您的连接(来自使用的两个数据上下文)指向不同的数据库,即使在SQL Server 2008上,您的TransactionScope也会升级为分布式事务。

将应用程序注册到DTC至少有两种负面影响:

  • 吞吐量会急剧下降。一个数据库可以支持每秒几千个本地事务,但只能支持每秒几十个(也许是低百个)分布式事务。主要原因是 两阶段提交 的复杂性。
  • DTC 需要一个协调器:MSDTC。对 MSDTC 进行的 [安全增强] 使得配置更具挑战性,而且开发人员可能会惊讶地发现他们的应用程序需要 MSDTC。 链接文章中描述的步骤可能是您目前缺少的内容。对于 Windows Vista/Windows 7/Windows Server 2008/Windows Server 2008R2,这些步骤在 Windows Vista 和 Windows Server 2008 中的 MSDTC如何在 Windows 2008 上配置 DTC 和其他类似的文章中有描述。
现在,如果您按照上述文章修复了MSDTC通信问题,则您的代码应该可以正常工作,但我仍然认为这个归档过程不应该发生在运行EF的客户端代码中。有更好的工具,SSIS就是一个很好的例子。定期运行SSIS的夜间任务将更有效地转移那些未使用的配置文件。

这非常有帮助。我会把它作为答案。 - voddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接