C# 如何处理BL中的嵌套datacontext?

C# 如何处理BL中的嵌套datacontext?,c#,linq-to-sql,nested,datacontext,transactionscope,C#,Linq To Sql,Nested,Datacontext,Transactionscope,这是嵌套DataContext(未测试)的一个示例。当一个TransactionScope被添加到我们的小实验中(如上所示)时,问题就开始了。AddFolder函数中的第一个AddFile会将事务升级到DTC(这绝对是错误的),因为AddFile会初始化新的DataContext,从而打开到DB的第二个连接 如何使用不会在DTC使用中出现的嵌套DataContext 这完全是错的吗?我应该以不同的方式使用DataContext吗 毫无疑问,应尽可能避免升级到DTC。当我第一次阅读您的问题时,我的

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

  • 如何使用不会在DTC使用中出现的嵌套DataContext
  • 这完全是错的吗?我应该以不同的方式使用DataContext吗

  • 毫无疑问,应尽可能避免升级到DTC。当我第一次阅读您的问题时,我的直觉告诉我您的事务不会升级到DTC,因为您在两个数据上下文中使用相同的连接字符串。然而,据报道,我错了

    您并不是唯一一个在数据上下文中采用过多最佳实践的人。如果你在网上搜索,地图上到处都是答案。在您的示例中,可以将数据上下文传递到AddFile方法中。或者,您可以将此数据访问重构为一个类,该类将保持数据上下文的生存期,直到文件夹和文件全部保存。里克·斯特拉尔介绍了几种技术


    尽管如此,我在LinqtoSQL中看到的答案似乎都不是很令人满意。您是否考虑过使用ORM来避免数据层的管理?我已经成功地使用了它,但我听说了它的优点。这两者都需要,但也有。

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

    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
    }
    
    实现的控制器的简单使用

    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
    }
    
    更复杂地使用已实现的控制器(同一DataContext上的两个控制器)


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

    您不需要为此事务进行两次或更多的往返。我相信LINQ DataContext能够智能地识别这些文件属于文件夹对象,并将首先插入文件夹行,然后插入文件(事务上下文中的所有内容,例如BEGIN TRAN/COMMIT)。但是,您需要执行以下操作:

    var testControllerA = new BLTestControllerA();
    var testControllerB = new BLTestControllerB(testControllerA.CurrentDataContext);
    
    testControllerA.DeleteById(1);
    testControllerB.DeleteById(1);
    
    而不是FolderId。大概是这样的:

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

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

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


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

    这是查看它的一种方法。不幸的是,我不会很快改变我的ORM,所以我必须找到解决问题的正确方法。看来改变ORM将是明智的解决方案,因为Microsoft提供了一个非常基本的解决方案,不适合企业的需要,所以我切换到了NHibernate:)您在这里给出的链接完美地解释了事务范围/DTC关系,尽管它与我所问的有点不同。我认为不可能传递连接属性,但也许你的意思是其他的-你能添加一个代码示例吗?@Eran:DataContext对象有一个重载构造函数,它接受任何IDbConnection对象。因此,您可以创建新的DataContext(OldDataContext.Connection)。不幸的是,我觉得这个问题没有得到应有的重视,并且没有得到充分的回答:(
    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();
            }
        }
    }