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的第二个连接。
答案 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个连接)。