7
public class TestBL
{
    public static void AddFolder(string folderName)
    {
        using (var ts = new TransactionScope())
        {
            using (var dc = new TestDataContext())
            {
                var folder = new Folder { FolderName = folderName };

                dc.Folders.InsertOnSubmit(folder);
                dc.SubmitChanges();

                AddFile("test1.xyz", folder.Id);
                AddFile("test2.xyz", folder.Id);
                AddFile("test3.xyz", folder.Id);

                dc.SubmitChanges();
            }

            ts.Complete();
        }
    }

    public static void AddFile(string filename, int folderId)
    {
        using (var dc = new TestDataContext())
        {
            dc.Files.InsertOnSubmit(
                new File { Filename = filename, FolderId = folderId });

            dc.SubmitChanges();
        }
    }
}

这是嵌套 DataContext 的示例(未经测试)。当一个 TransactionScope 添加到我们的小实验中时,问题就开始了(如上所示)。AddFolder 函数中的第一个 AddFile 会将事务升级到 DTC(这绝对是不好的),因为 AddFile 初始化了新的 DataContext,从而打开了与 DB 的第二个连接。

  1. 如何使用不会发生 DTC 使用的嵌套 DataContext?
  2. 这一切都是错的吗?我应该以不同的方式使用 DataContext 吗?
4

4 回答 4

3

毫无疑问,应尽可能避免升级为 DTC。当我第一次阅读您的问题时,我的直觉说您的事务不会升级到 DTC,因为您在两个数据上下文中使用相同的连接字符串。但是,根据这篇文章,我错了。

对数据上下文的最佳实践感到困惑的不止您一个人。如果你在网上搜索这个,地图上到处都有答案。在您的示例中,您可以将数据上下文传递给 AddFile 方法。或者,您可以将此数据访问重构为一个类,该类维护数据上下文的生命周期,直到文件夹和文件全部保存为止。Rick Strahl 发表了一篇关于几种技术的文章。

尽管如此,我在 LINQ to SQL 周围看到的答案似乎都不是很令人满意。您是否考虑过使用 ORM 来避免管理数据层?我使用NetTiers取得了巨大的成功,但我听到了关于PLINQO的好消息。这些都需要CodeSmith,但有很多替代方法

于 2009-08-29T20:21:37.840 回答
1

除了将 DataContext 作为参数传递给 AddFiles 之外,您还可以将一个 DataContext 的 Connection 值传递给另一个 DataContext。这将保证另一个 DataContext 具有相同的连接。

每个 DataContext 也有一个 Transaction 属性,您可以设置和传递它而不是使用 TransactionScope 对象。

于 2009-09-16T04:53:11.583 回答
0

我想出了一种处理这种情况的方法。

BL 实体的示例基类(实体将继承此类)

abstract public class TestControllerBase : IDisposable
{
    public TestDataContext CurrentDataContext { get; private set; }

    protected TestControllerBase()
    {
        CurrentDataContext = new TestDataContext();
    }

    protected TestControllerBase(TestDataContext dataContext)
    {
        CurrentDataContext = dataContext;
    }

    protected void ClearDataContext()
    {
        CurrentDataContext.Dispose();
        CurrentDataContext = new TestDataContext();
    }

    public void Dispose()
    {
        CurrentDataContext.Dispose();
    }
}

实现的控制器

public sealed class BLTestController : TestControllerBase
{
    public BLTestController() { }

    public BLTestController(TestDataContext dataContext)
        : base(dataContext) { }

    //  The entity functions will be implemented here using CurrentDataContext
}

简单使用已实现的控制器

var testController = new BLTestControllerA();

testController.DeleteById(1);

更复杂地使用已实现的控制器(同一 DataContext 上的 2 个控制器)

var testControllerA = new BLTestControllerA();
var testControllerB = new BLTestControllerB(testControllerA.CurrentDataContext);

testControllerA.DeleteById(1);
testControllerB.DeleteById(1);

我想看到更多关于解决这个谜题的想法和关于上面代码的评论。

于 2009-09-06T00:41:43.267 回答
0

您不需要为此交易执行 2 次或更多往返。我相信 LINQ-DataContext 很聪明地识别出这些文件属于文件夹对象,并且会先插入文件夹行,然后插入文件(事务上下文中的所有内容,例如 BEGIN TRAN/COMMIT)。但是,您需要这样做:

dc.Files.InsertOnSubmit(
                new File { Filename = filename, Folder = folder });

而不是 FolderId。像这样的东西:

public class TestBL
{
    public static void AddFolder(string folderName)
    {
        using (var ts = new TransactionScope())
        {
            using (var dc = new TestDataContext())
            {
                var folder = new Folder { FolderName = folderName };

                AddFile(dc, "test1.xyz", folder);
                AddFile(dc, "test2.xyz", folder);
                AddFile(dc, "test3.xyz", folder);

                dc.SubmitChanges();
            }

            ts.Complete();
        }
    }

    private static void AddFile(DataContext dc, string filename, Folder folder)
    {
            dc.Files.InsertOnSubmit(
                new File { Filename = filename, Folder = folder });
    }

    public static void AddFile(string filename, int folderId)
    {
        using (var dc = new TestDataContext())
        {
            var folder = new Folder { FolderId = folderId };
            dc.Attach(folder, false);
            AddFile(dc, filename, folder);

            dc.SubmitChanges();
        }
    }
}

对于您关于 DTC 的问题:由于 2 个打开的连接,我认为这里不可能避免 DTC。我相信他们最近在这方面做了一些改变,看这里,但是描述的场景有点不同(2 个连接一个接一个地打开和关闭,而不是同时打开 2 个连接)。

于 2009-09-13T17:21:32.883 回答