EF4、TransactionScope 和 Task<>
是否可以打开一个 TransactionScope
,运行一批在 EF4 ObjectContext
上操作的异步 Task
,然后提交结果?
EF4中如何推断当前事务范围?如果/当任务被安排在与事务范围不同的线程上时,这会失败吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的。对于初学者来说,实体框架仅使用下面的提供程序(默认为 System.Data.SqlClient),它将从正在执行的线程中获取“环境”事务上下文。因此,从这里开始,唯一的技巧是将单个事务传播到您启动的任务。我已经在这篇文章中解释了如何做到这一点。
虽然该文章更多的是关于传播到 PLINQ 生成的任务,但如果您手动启动自己的
任务
,则同样的方法也适用。如果您想要代码示例,请向我提供您的Task
生成方式的基本详细信息,以便我可以提供良好的示例代码。Yes, it is. For starters, Entity Framework just uses a provider underneath (by default System.Data.SqlClient) which will pick up the "ambient" transaction context from the thread that is executing. So, from there, the only trick is propagating the single transaction to the
Tasks
you spin up. I have explained how you can do that here in this post.While that post was more about propgating to PLINQ spawned tasks, the same approach is applicable if you're manually spinning up your own
Tasks
. If you would like a code sample, please give me basic details of exactly how yourTask
spawning would work so I can give good example code.不,你不能(有效地)这样做。
尽管 Drew Marsh 的答案是正确的(存在使事务跨线程边界的方法),但它不会对您有帮助。
ObjectContext
不是线程安全的 - 您不应该从其他线程访问它,并且您绝对不应该在其他线程上更新它;你将会有未定义的行为:你可能会遇到数据损坏,这(如果你幸运的话)会导致崩溃。如果您想要多线程
ObjectContext
访问,则需要手动序列化访问,例如使用锁。但如果您这样做,您也可以简单地从一个线程访问上下文;它通常更简单,而且几乎总是更快 - 然后您的交易就不会有任何问题。如果您坚持手动同步对
ObjectContext
的访问而不是使用线程,您还可以使用普通的CommittableTransaction
并显式传递它,而不是使用环境事务;因为无论如何您都需要手动控制事务,所以使用对象的显式句柄而不是棘手的状态转换(其中哪个线程运行重要但在代码中不明确的线程的确切细节)来做到这一点更清楚。顺便说一句,如果您确实使用环境事务,我会小心任务调度,特别是使用 C# 5 的异步功能,因为您可能需要清楚地了解执行何时可以更改线程(我从未尝试过这个,所以不幸的是我无法给你任何指示)。
总结:不要这样做:由于
ObjectContext
(实际上是数据库)的限制,您无法通过多线程获得并发性,因此,您不妨将一项事务保留在一个线程上并保持简单。未来的维护者会感谢您的清晰说明。No, you cannot (usefully) do this.
Although Drew Marsh's answer is correct (that there exist means to make the transaction cross thread boundaries), it's not going to help you.
ObjectContext
is not thread safe - you should not be accessing it from other threads, and you should definitely not be updating it on other threads; you'll have undefined behaviour: you'll probably encounter data-corruption, which (if you're lucky) will cause crashes.If you want multi-thread
ObjectContext
access, you'll need to manually serialize the accesses for example using locks. But if you do that, you might as well simply access the context from one thread; it's usually simpler and almost always faster - and then you'll have no issues with your transactions.If you insist on manually synchronizing access to the
ObjectContext
rather than using a thread, you could also use a plainCommittableTransaction
and pass that along explicity rather than using the ambient transaction; since you'll need to manually control the transaction anyhow, it's clearer to do so with the explicit handle of an object rather than tricky state-transitions (where the exact details of which thread runs what are vital but not explicit in the code).By the way, if you do use the ambient transaction, I'd be careful with task scheduling, especially with C# 5's async feature, as you may need to be clearly aware of when execution can change thread (I've never tried this, so I can't give you any pointers, unfortunately).
Summary: just don't do this: you're not gaining concurrency by multithreading due to
ObjectContext
(and in practice, DB) limitations, so you might as well leave one transaction on one thread and keep it simple. Future maintainers will thank you for the clarity.