如何设计工作单元以支持批量操作并提供更多性能?
Posted
技术标签:
【中文标题】如何设计工作单元以支持批量操作并提供更多性能?【英文标题】:How to design unit of work to support bulk operations and give more performance? 【发布时间】:2014-06-19 10:53:53 【问题描述】:我有 2 个不同的工作单元:一个基于 ADO.NET,主要调用存储过程 (uowADO
),另一个使用 Entity Framework 6 (@ 987654323@),最近添加以支持 Oracle db,这样我就不必重写所有的 SP(我的知识有限)。
因此,在对数据库执行操作时,业务层仅加载其中一个(基于配置)(但我不能并行使用它们,因为uowADO
不支持 Oracle)
添加新的uowEF
后,我注意到很大的性能问题,当然主要是批量操作。
基本上我现在在当前的IUnitOfWork
上只有Commit
和Rollback
方法......非常接近article 推荐的方法。
所以,我正在考虑重新设计这个工作单元。例如,我读到了有时在涉及批量操作时禁用dbContext.Configuration.AutoDetectChangesEnabled
,以及其他有关 EF 的优化技巧可能会有所帮助。
不幸的是,我不确定如何设计这样的工作单元以使其通用,以便我可以在所有情况下从 BL 和两个数据访问层使用它:ADO.NET 和 EF.
对此有什么想法、建议、好的链接吗?
【问题讨论】:
域事件模式,最终一致性。但是如果你不熟悉 DDD 和事件驱动架构,这并不容易 【参考方案1】:没有明确的答案。它始终是灵活性和性能之间的折衷。所有这些模式(存储库、工作单元)都有利于灵活性,但不利于性能,因为具体实现通常需要一些调整以提供最大性能,而这些调整可能不(实际上它们不会)与通用接口兼容。您可以调整接口,但它可能不适用于其他实现。所有这些 ORM 都非常不同,很难(几乎不可能)实现通用存储库/UOF 接口来支持所有这些,特别是如果您有 ADO(低级 ORM,实际上很难称其为 ORM)和 EF(高级 ORM)。将 AutoDetectChangesEnabled 设置为 false 只是您可以使用 EF 执行的一小部分操作。要从中获得更多性能,您还必须以特定方式实现实体(添加一些属性,一些属性)。如果我们看一下 Linq2Sql(另一个 ORM),它需要编译查询,所以,忘记这样的方法:
T Single(Expression<Func<T, bool>> predicate);
在您的存储库中。我只是在谈论关系数据库的存储库。 NoSql 数据库的存储库呢?基于完全不同数据源的存储库呢?是的,可以提供具有通用逻辑的通用接口,但是对于某些数据源来说会很慢。如果您真的想实现通用解决方案并获得最大性能,您的 UOF 存储库应该具有非常具体的接口,例如:
IEnumerable<T> GetStudentsByName(srting name)
void InsertStudents<T>(IEnumerable<T> students)
其中 T - 不应依赖于具体存储库实现的业务级实体。存储库应负责将 T 转换为它实现访问的 ORM 可接受的实体。但请记住,解决方案将过于复杂且难以支持。
如果我是你,我会选择一个最符合我要求的主要 ORM,并围绕这个 ORM 设计存储库以从中获得最大的性能。但是,我将保持接口足够灵活,以便有可能实现对其他数据源或 ORM 的至少缓慢(但有效)的访问。
【讨论】:
最后一些输入证实了我的想法......谢谢!我以为我走错了路,因为很难找到一个好的解决方案。你是绝对正确的,我实际上按照你的建议实施了一切。所以,就目前而言,我只有一个适用于 ADO 和 EF 的解决方案,但 EF 速度很慢......还有一个映射器,在我的域和 EF 实体之间有额外的缓存。至少现在我知道我离我不远了。【参考方案2】:不幸的是,我不确定如何设计这样的工作单元来实现它 通用的,这样我就可以在所有情况下从 BL 和两种数据中使用它 访问层:ADO.NET 和 EF。
首先要意识到 EF 无法访问数据库。它依赖于 ADO.NET。最后,它使用命令、数据读取器等来完成底层工作。
以这样一种方式进行设置是很简单的,您可以在 EF 上下文中提取连接,然后自己直接使用 ADO.NET。
由于 EF 以任何方式对执行高效甚至低容量的批量操作的支持为零 - 无论如何,您可以/必须依赖 ADO.NET。从 SQL 的角度来看,EF 本身就是一个完美的反例,它可以帮助您处理更大容量的数据库。它每行使用一个更新/插入 - 尽管(例如,即使忽略 SqlBulkCopy)它可以为大多数数据库发出一个多行,因为语法完全允许这样做。
如愿以偿:当您喜欢阅读示例文档时,获得底层连接真的很简单。这是我用于批量插入的代码。 ObjectBulkCopy 类稍微复杂一些,但这里可以看到如何获取数据库连接:
public static void BulkInsert<T>(this Repository repository, IEnumerable<T> objects) where T : class
var bulkCopy = new ObjectBulkCopy<T>();
var connection = (SqlConnection) repository.Database.Connection;
bulkCopy.Insert (objects, connection);
非常琐碎 - 它作为属性存在于任何存储库中,可以转换。
【讨论】:
您是说“自己直接使用 ADO.NET”... 不确定我是否清楚地看到了如何... 您能给我看一些伪代码吗?也许我的问题中还应该有一些伪代码来显示我到目前为止所拥有的内容。 不需要伪代码。您所需要的只是连接......而且很容易获得。添加代码。 我知道如何获得连接,但我不明白你想如何获得这个。现在我看到您正在对存储库使用“BulkInsert”方法。实际上,我在 repo 中只有一个“插入”功能。如果我想执行多次插入,我打开一个 sql 事务并使用 BL 中的循环。 虽然我 100% 支持你,但我不确定它是否完全回答了 OP 问题。注意 Oracle 也有一个 OracleBulkCopy 类。 那你为什么说批量?那是最慢的方法。好吧,做吧。我们无法帮助您根据复杂的场景做出决策 - 这是问答网站,而不是“为您做我的架构”服务。以上是关于如何设计工作单元以支持批量操作并提供更多性能?的主要内容,如果未能解决你的问题,请参考以下文章