There is quite a bit of Entity Framework vs NHibernate comparisons on the web already, but all of them cover mostly the technical side of the question. In this post, I’ll compare these two technologies from a Domain Driven Design (DDD) perspective. I’ll step through several code examples and show you how both of these ORMs let you deal with problems.

Onion architecture

Onion architecture

Nowadays, it is common to use onion architecture for complex systems. It allows you to isolate your domain logic from other pieces of your system so that you can focus your effort on the problems that are essential to your application.

Domain logic isolation means that your domain entities should not interact with any classes except other domain entities. It’s one of the core principles you should follow to make your code clean and coherent. Of course, it’s not always possible to achieve that level of isolation but it’s certainly worth trying.

The picture below shows onion architecture using the classic n-tier scheme.

Onion architecture from the N-tier perspective

Persistence Ignorance

While using an ORM, it is vital to sustain a good degree of persistence ignorance. It means that your code should be structured in such a way that allows you to factor the persistence logic out of your domain entities. Ideally, domain classes should not contain any knowledge about how they are being persisted. That allows us to adhere to the Single Responsibility Principle and thus keep the code simple and maintainable.

If you tend to write code like in the sample below, you are on the wrong path:

public class MyEntity

{

// Perstisted in the DB

public int Id { get; set; }

public string Name { get; set; }

// Not persisted

public bool Flag { get; set; }

}

If your domain logic is separated from persistence logic, you can say that your entities are persistence ignorant. That means that you can change the way you persist their data without affecting the domain logic. Persistence ignorance is prerequisite to the domain logic isolation.

Case #1: Deleting an entity from aggregate root

Let’s go through the real-world code examples.

Order aggregate

Here is an aggregate that contains two classes at the diagram above. Order class is the aggregate root, which means that Order controls the Lines collection’s lifespan. If an Order is deleted, its Lines are deleted with it; OrderLine has no meaning without an Order.

Let’s assume that we want to implement a method that deletes a single line from an order. Here is how it can be implemented if you don’t have to save your entities in the database (i.e. without any ORM):

public ICollection<OrderLine> Lines { get; private set; }

public void RemoveLine(OrderLine line)

{

Lines.Remove(line);

}

You just delete it from the collection, and that’s it. As long as Order is the root of the aggregate, any other classes must retrieve an Order’s instance in order to get access to its lines. If there’s no such line, we can say that it is deleted.

Now, if you try to do it with Entity Framework, you’ll get an exception:

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable.

There’s no way to instruct Entity Framework to delete orphaned items from the database. You need to do that yourself:

public virtual ICollection<OrderLine> Lines { get; set; }

public virtual void RemoveLine(OrderLine line, OrdersContext db)

{

Lines.Remove(line);

db.OrderLines.Remove(line);

}

Passing OrdersContext parameter to the entity’s method breaks the Separation of Concerns principles, because the Order class becomes aware of how it is stored in the database.

Here’s how it can be done in NHibernate:

public virtual IList<OrderLine> Lines { get; protected set; }

public virtual void RemoveLine(OrderLine line)

{

Lines.Remove(line);

}

Note that the code is almost identical to the code that is not bound to any ORM. You can instruct NHibernate to delete the orphaned line from the database by using this mapping:

public class OrderMap : ClassMap<Order>

{

public OrderMap()

{

Id(x => x.Id);

HasMany(x => x.Lines).Cascade.AllDeleteOrphan().Inverse();

}

}

Case #2: Link to a related entity

Order line reference

Let’s say you need to introduce a link to a related entity. Here’s a code that is not bound to any ORM:

public class OrderLine

{

public Order Order { get; private set; }

// Other members

}

This is the default way to do that with Entity Framework:

public class OrderLine

{

public virtual Order Order { get; set; }

public int OrderId { get; set; }

// Other members

}

The default way to do that in NHibernate:

public class OrderLine

{

public virtual Order Order { get; set; }

// Other members

}

As you can see, by default, Entity Framework requires an additional Id property to work with the reference. This approach violates SRP because Ids represent an implementation detail of how entities are stored in a database. Entity Framework impels you to work directly with the database’s concepts whereas a better approach in this situation would be just a single Order property.

Moreover, this code breaks the DRY principle. Declaring both OrderId and Order properties enables OrderLine class to easily fall into an inconsistent state:

Order = order; // An order with Id == 1

OrderId = 2;

Now, EF does allow for declaring a single Order property instead. But still, there are two problems with it:

  • Accessing the Id property of the related entity leads to loading that entity from the database, although that Id is already in memory. In contrary, NHibernate is smart enough to not do that. With NHibernate, the loading would be executed only if you refer to an Order’s property other than Id.
  • Entity Framework impels developers to use Ids instead of just related entity reference. Partly, because it’s the default way of doing this sort of things, and partly because all the examples use this approach. In contrast to Entity Framework, the default way to create a reference in NHibernate is the first one.

Case #3: Read-only collection of related entities

If you need to make the lines collection read-only for the Order’s clients, you could write this code (not bound to any ORM):

private List<OrderLine> _lines;

public IReadOnlyList<OrderLine> Lines

{

get { return _lines.ToList(); }

}

The official way of doing it in EF:

public class Order

{

protected virtual ICollection<OrderLine> LinesInternal { get; set; }

public virtual IReadOnlyList<OrderLine> Lines

{

get { return LinesInternal.ToList(); }

}

public class OrderConfiguration : EntityTypeConfiguration<Order>

{

public OrderConfiguration()

{

HasMany(p => p.LinesInternal).WithRequired(x => x.Order);

}

}

}

public class OrdersContext : DbContext

{

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

modelBuilder.Configurations.Add(new Order.OrderConfiguration());

}

}

Clearly, this is not how you’d like to build your domain entities as you directly include an infrastructure class in your domain class. Unofficial solution is hardly better:

public class Order

{

public static Expression<Func<Order, ICollection<OrderLine>>> LinesExpression =

f => f.LinesInternal;

protected virtual ICollection<OrderLine> LinesInternal { get; set; }

public virtual IReadOnlyList<OrderLine> Lines

{

get { return LinesInternal.ToList(); }

}

}

public class OrdersContext : DbContext

{

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

modelBuilder.Entity<Order>()

.HasMany(Order.LinesExpression);

}

}

Still, you need to include infrastructure code in the Order class. Entity Framework doesn’t offer anything to factor this logic out.

Here’s how it can be done with NHibernate:

public class Order

{

private IList<OrderLine> _lines;

public virtual IReadOnlyList<OrderLine> Lines

{

get { return _lines.ToList(); }

}

}

public class OrderMap : ClassMap<Order>

{

public OrderMap()

{

HasMany<OrderLine>(Reveal.Member<Order>(“Lines”))

.Access.CamelCaseField(Prefix.Underscore);

}

}

Again, the way we do it with NHibernate is almost identical to the way we would do it without any ORM whatsoever. The Order class here is clean and doesn’t contain any persistence logic, and thus allows us to focus on the domain, dealing with one problem at a time.

NHibernate’s approach has one drawback, though. This code is vulnerable to refactoring as the property’s name is typed in as a string. I consider it to be a reasonable trade-off because it allows us to better separate the domain code from the persistence logic. Besides, such errors are really easy to detect either by manual testing or integration auto tests.

Case #4: Unit of Work pattern

Here’s another example. Below is the code from a task I worked on a couple years ago. I’ve omitted details for brevity, but you should get the point:

public IList<int> MigrateCustomers(IEnumerable<CustomerDto> customerDtos,

CancellationToken token)

{

List<int> ids = new List<int>();

using (ISession session = CreateSession())

using (ITransaction transaction = session.BeginTransaction())

{

foreach (CustomerDto dto in customerDtos)

{

token.ThrowIfCancellationRequested();

Customer customer = CreateCustomer(dto);

session.Save(customer);

ids.Add(customer.Id);

}

transaction.Commit();

}

return ids;

}

The method takes some data, converts it to domain objects and saves them after that. It returns a list of customers’ Ids. The caller code has an ability to cancel the migration process using CancellationToken which is passed in as well.

If you use Entity Framework for this task, it will insert your entities in the database in order to get their Ids because, for integer identifiers, EF doesn’t allow for choosing any id generation strategy other than database identity. This approach works well for most of the cases, but it has a major flaw – it breaks the Unit of Work pattern. If the method is canceled, EF would have to delete all the records that have been inserted so far, which itself causes massive performance impact.

With NHibernate, you can choose Hi/Lo id generation strategy so that customers are simply not saved until the session is closed. Ids are generated on the client side, so there’s no need to touch the database to retrieve them. NHibernate can save a huge amount of time with this type of tasks.

Case #5: Working with cached objects

Let’s say your customer list is pretty stable and don’t change often. You may decide to cache them so that you can use customer list without querying the database. Now, let’s also say that the Order class has a precondition to belong to one of the customers. This precondition can be implemented using a constructor:

public Order(Customer customer)

{

Customer = customer;

}

This way, we are sure that no order can be created without a customer.

With Entity Framework, you can’t set a detached object as a reference to a new object. If you use a constructor like the one above, EF will try to insert the customer in the database because it wasn’t attached to the current context. To fix it, you need to explicitly specify that the customer is already in the database:

public Order(Customer customer, OrdersContext context)

{

context.Entry(customer).State = EntityState.Unchanged;

Customer = customer;

}

Again, such approach breaks SRP. In contrast, NHibernate can detect an object’s state by its Id and doesn’t try to insert this object if it’s already in the database, so the NHibernate’s version would be the same as the non-ORM one.

Entity Framework vs NHibernate: results

There’s one simple method of how to measure an ORM’s persistence ignorance degree. The closer the code that uses an ORM is to a code that isn’t bound to any database, the cleaner this ORM, and the more it is persistence ignorant.

EF is too tightly coupled to the database notions. When you model your application with Entity Framework, you still need to think in terms of foreign-key constraints and table relationships. Without a clean and isolated domain model, your attention is constantly distracted, you can’t focus on the domain problems that are essential for your software.

The trick here is that you actually may not notice such distractions until your system grows in size. And when it does, it becomes really hard to sustain the pace of development because of increased maintainability costs.

That said, NHibernate is still way ahead of Entity Framework. Besides the better separation of concerns, NHibernate has a bunch of useful features that Entity Framework doesn’t: 2nd level cache, concurrency strategies, rich mapping capabilities and so on and so forth. Probably, the only feature that EF can boast of is async database operations.

Why is it so? Isn’t Entity Framework being actively developed for many years? EF was initially created as a tool for working on simple CRUD applications and only several years after got some really good features, such as code-first mapping. It seems that this initial purpose is still leaking out, even after all these years of active development. It means that while Entity Framework is a good tool in many situations, if you want to create a domain model which fully adheres to the DDD principles, you are still better off choosing NHibernate.

I believe Entity Framework can replace NHibernate eventually, but the EF team will need to adjust its approach to building the ORM in order to achieve it (although, admittedly, they did a very good job since the version 1).

Update 12/1/2014

The Entity Framework program manager Rowan Miller replied that some of this issues will be addressed in EF7, although, still, not all of them. Nevertheless, EF seems to be going in a right direction, so let’s cross our fingers.

If you want to learn more about how to build a rich domain model having an ORM at hand, you can watch my Pluralsigh course Domain-Driven Design in Practice.

from:http://enterprisecraftsmanship.com/2014/11/29/entity-framework-6-7-vs-nhibernate-4-ddd-perspective/

Entity Framework 6 (7) vs NHibernate 4: DDD perspective(纯净DDD很难很难...)的更多相关文章

  1. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  2. Entity Framework 学习之--Ling to entity实现分页

    最近用MVC做的一个项目涉及到分页,中间用了entity framework来查数据库,不用直接写sql语句,方便了很多. 一般分页的思路是获得两个变量的值: 1.一共有多少条记录 totalCoun ...

  3. 全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

    在开发涉及到数据库的程序时,常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况,这时就需要进行数据库迁移. 实现数据库迁移有很多种办法,从手动管理各个版本的ddl脚本,到实现自己的mig ...

  4. ADO.NET、NHibernate和Entity Framework的比较

    ---原文地址:http://www.xuebuyuan.com/2162973.html 1,ADO.NET属于传统的数据访问工具,开发的时候需要我们手动去编写操作数据库的各种操作,当然性能也就不用 ...

  5. Entity Framework与ADO.Net及NHibernate的比较

    Entity Framework  是微软推荐出.NET平台ORM开发组件, EF相对于ado.net 的优点 (1)开发效率高,Entity Framework的优势就是拥有更好的LINQ提供程序. ...

  6. 比较NHibernate和Entity Framework

    葡萄牙的一位开发者 Ricardo Peres 最近发布了一篇文章,以看起来无偏见的形式对领先的两种 .NET ORM:NHibernate 和 Entity Framework 进行了比较. 我们建 ...

  7. csharp: NHibernate and Entity Framework (EF) (object-relational mapper)

    代码生成器: 1. http://www.codesmithtools.com/ 2.https://sourceforge.net/projects/mygeneration/ 3. http:// ...

  8. DDD, MVC & Entity Framework

    https://digitalpolis.co.uk/software-thoughts/ddd-mvc-entity-framework/ Data Points - Coding for Doma ...

  9. Entity FrameWork 与 NHibernate

      1 Nhibernate 展示了NHibernate在数据库和用程序之间提供了一个持久层. 应用程序自己提供ADO.NET连接,并且自行管理事务.NHibernate体系结构如图1-51所示.它体 ...

随机推荐

  1. python3之模板pycurl探测web服务质量

    1.pycurl简介 pycURL是libcurl多协议文件传输库的python接口,与urllib模块类似,PycURL可用于从python程序中获取由URL标识的对象,功能很强大,libcurl速 ...

  2. Transition学习笔记

    概述 Android 4.4.2 (API level 19)引入Transition框架,之后很多APP上都使用该框架做出很酷炫的效果,如 Google Play Newsstand app 还有g ...

  3. CSS之:active选择器

    Active的一段话 active的英文解释为“积极的”,表现在鼠标上就是点击的意思.关于active选择器最多的示例恐怕就是应用在链接上面的,然而打开链接是一个一瞬间的动作,这不能很好的体现acti ...

  4. CentOS 安装codeblocks

    1. 安装wxWidgets .tar.bz2 [root@luozhonghua codeblocks]# cd wxWidgets- [root@luozhonghua wxWidgets-]# ...

  5. PHP 5.2、5.3、5.4、5.5、5.6 对比以及功能详解

    php5.2.x php5.3.x php5.4.x php5.5.x php5.6.x 对比详解 截至目前(2014.2), PHP 的最新稳定版本是 PHP5.5, 但有差不多一半的用户仍在使用已 ...

  6. java 的反射机制

    一:介绍 1.大纲 #1 允许程序在执行期间,调用反射API取得任何类的内部信息,并且可以直接操作任何对象的内部属性和方法. #2 学习反射,需要掌握的知识点: *实例化class类 *获取类的完整结 ...

  7. Zookeeper项目开发环境搭建(Eclipse\MyEclipse + Maven)

    写在前面的话 可详细参考,一定得去看 HBase 开发环境搭建(Eclipse\MyEclipse + Maven) 我这里,相信,能看此博客的朋友,想必是有一定基础的了.我前期写了大量的基础性博文. ...

  8. webpack+vue-cli中代理配置(proxyTable)

    在做vue的项目时,用到webpack打包工具,我们会发现本地开发开启的node服务地址请求接口的地址存在跨域问题.本地开启的服务地址是 http://localhost:8080 而服务器的地址是 ...

  9. mysql分库分表那些事

    为什么使用分库分表? 如下内容,引用自 Sharding Sphere 的文档,写的很大气. <ShardingSphere > 概念 & 功能 > 数据分片> 传统的 ...

  10. MySQL数据库之索引

    1 引言 在没有索引的情况下,如果要寻找特定行,数据库可能要遍历整个数据库,使用索引后,数据库可以根据索引找出这一行,极大提高查询效率.本文是对MySQL数据库中索引使用的总结. 2 索引简介 索引是 ...