如何在BL中处理嵌套的datacontext?

时间:2009-08-29 16:08:15

标签: c# linq-to-sql nested datacontext transactionscope

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 个答案:

答案 0 :(得分:3)

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

confusion数据上下文的最佳实践中,您并不孤单。如果您在网上搜索此内容,则地图上会有答案。在您的示例中,您可以将数据上下文传递给AddFile方法。或者,您可以将此数据访问重构为维护数据上下文生命周期的类,直到文件夹和文件全部保存为止。 Rick Strahl发表了an article几种技巧。

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

答案 1 :(得分:1)

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

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

答案 2 :(得分: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);

我希望看到有关解决这个问题的更多想法以及对上述代码的评论。

答案 3 :(得分: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。我相信他们最近在这个领域做了一些改变,见here,但是描述的场景有点不同(2个连接一个接一个地打开和关闭,而不是同时打开2个连接)。