如何抽象 NHibernate 以避免紧密依赖并促进测试
是否可以采用像NHibernate或Entity Framework这样的O/RM,并以这样的方式进行抽象,以便在遇到O/RM无法处理的情况时可以替换它。
创建一个具有大块服务方法的服务似乎很诱人,在该服务中创建一个会话,该会话用于获取/更新插入实体,然后用于保存所有脏对象。
我会考虑存储库模式,以便服务操作向存储库请求实体,并且 O/RM 会话嵌入在存储库中。但是,如何保存相关实体,以及 Update(T 实体) 方法是否立即刷新更改。它看起来很简单,而且通常不能令人满意。
我现在倾向于的是一个单一的 O/RM 包装类,它公开一个具有通用方法的接口,如“StartSession”、“EndSession”、“AbandonSession”、“GetById(object id)”等。
至少这将允许 OR /M 在测试中被伪造,这是我的另一个大问题。
我想我是说我不想将业务逻辑和 O/RM 数据访问代码紧密地交织在一起,因为切换到另一个 O/RM 可能会导致大部分代码被替换。
人们在现实世界中做什么?
Is it possible to adopt the use of an O/RM like NHibernate or Entity Framework, and abstract it in such a way that it could be replaced if a situation is encountered that the O/RM cannot handle.
It seems tempting to create a service with chunky service methods inside of which, a session is created, the session is used to get / upsert entities and is then used to save all dirty objects.
I would have considered the repository pattern so that the service operation asks the repository for entities, and the O/RM session is embedded in the repository. But what do you do about saving related entities, and does an Update(T entity) method flush changes imediately. It seems to simple and generally unsatisfactory.
What I am leaning toward now is a single O/RM wrapper class that exposes an interface with generic methods like "StartSession", "EndSession", "AbandonSession", "GetById(object id)" etc.
At least this would allow the OR/M to be faked in testing which is another big concern of mine.
I guess I'm saying that I don't want to closely intertwine business logic, and O/RM data access code, because switching to another O/RM could cause most of that code to be replaced.
What do people do in the real world?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我强烈的意见是对较小的项目进行一些测试,并选择一个预先使用的工具集,而不是通过构建一个“有一天”能够更改的抽象包装器来推迟决定,直到稍后。根据我的经验,这一天永远不会到来,并且您引入的复杂性是不值得的。
我过去曾尝试为 nHibernate 构建一个通用包装器,但基本上除了痛苦之外什么也没遇到。 ORM 工具本身实际上是一个包装器和工具集。当您尝试创建包装器的包装器时,您最终需要开发(并随后维护)相对复杂的工具和机制来完成您要包装的事物本身的功能。
而且,根据我自己的经验,我花费大量时间构建的包装器最终只暴露了底层 ORM 工具的一小部分功能。然后我从来没有将包装器与除 nHibernate 之外的任何东西一起使用......所以我可以通过仅使用普通 nHibernate 本身来节省大量的时间和精力。
我建议仔细研究您的需求,而不是花时间抽象出瑞士军刀解决方案,而是进行研究,首先选择一个工具集并使用它运行。是的,您的代码将与第三方工具紧密耦合,但代码会更简单,您会更快地实现目标,并且结果可能会更好。
希望有帮助,
马克斯·席林
My strong opinion would be to do some tests on smaller projects and choose a toolset to use up front rather then put off the decision until later by building an abstract wrapper that you'll be able to change "someday". In my experience that someday never comes and the complexity you introduce is not worth it.
I have attempted to build a generic wrapper for nHibernate in the past and essentially met with nothing but pain because of it. ORM Tools are really a wrapper and toolset themselves. When you try to create a wrapper of a wrapper, you end up needing to develop (and subsequently maintain) relatively complex tools and mechanisms to accomplish what the thing you are wrapping does natively.
And, in my own experience, the wrapper I spent a LARGE amount of time building ended up only exposing a fraction of the abilities of the underlying ORM tools. And then I NEVER ended up using the wrapper with anything but nHibernate... so I could have saved myself massive amounts of time and effort by just using vanilla nHibernate itself.
I would recommend looking closely at what your requirements are and instead of spending the time abstracting out a swiss army knife solution, do the research, pick a toolset first and run with it. Yes your code will be tightly coupled to a third party tool, but the code will be simpler, you will get there faster and, likely, the results will be better.
Hope that helps,
Max Schilling
我使用工作单元模式处理数据库的 C/U/D 事务。在抽象方面我不会做更多的事情,因为我不想抽象抽象的抽象。 Linq 是查询部分的一个很好的抽象,通用存储库是查询部分的一个很好的抽象。这些是我在几乎所有具有关系数据库的项目中使用的抽象:
有时,我使用通用存储库,有时我使用具有规范模式支持的更具体的通用存储库,有时我为特定实体类型创建单独的存储库。
您可以在 NHibernate + 工作单元上搜索堆栈溢出,以查找有关实现这些模式的更多信息。
我不对映射进行抽象,因为每个orm在映射方面都有自己的特点,我认为映射已经是一个抽象了。我使用 Fluentnhibernate 进行映射。
I handle C/U/D transactions to the database with the unit of work pattern. I don't go much further than that in abstracting, because I don't want to abstract the abstraction of the abstraction. Linq is a good abstraction for the querying part, a generic repository is a good abstract for the querying part. These are the abstractions I use in almost any project with a relational database:
Sometimes, I use the generic repository, sometimes I use a more specific generic repository with specification pattern support, and sometimes I create a separate repository for a specific entity type.
You can search stack overflow on NHibernate + Unit of work to find more information about implementing these patterns.
I don't abstract the mapping, because every orm has it's own features in mapping, and I think the mapping is already an abstraction. I use fluentnhibernate for mapping.
一种选择是将您的域模型定义为 POCO,然后使用 BoostMap 在您的自定义类型和底层 OR/M。这使您可以根据需要交换底层 OR/M,并且还可以模拟一个 iQueryable 源以进行不依赖于数据库的单元测试:-)
One option is to define your domain model as POCOs, and then use BoostMap to proxy between your custom types and the underlying OR/M. This lets you swap out the underlying OR/M if needed, and also mock up an iQueryable source for unit testing that doesn't rely on the database :-)