C# – How should I design a 3-tier-architecture application with Entity Framework

cdesign-patternsentity-frameworkn-tierorm

This is almost getting me crazy. EF bring us a very convenience experience in development.

However, if we need to use the Entity model in the upper layer, we always put a reference from the upper layer to DAL or some layer at the lower which is holding the Entity class. I have visualize the situation.

                                     +-------------------+
                                     |                   |
                                     |   Presentation    |
    (This layer is directly referring to DAL, which break the 3-tier architecture rules)
                                     +-------------------+
                                               |
                                               |
                                               |
                                               |
                                     +---------v---------+
                                     |                   |
                                     |        BLL        |
                            (Function that returning Entity Model)
                                     +-------------------+
                                               |
                                               |
                                               |
                                               |
                                     +---------v---------+
                                     |                   |
                                     |        DAL        |
                                     (Repo & Entity Model)
                                     +-------------------+

Implement something like DTO at BLL is quite heavy and cannot solve this issue completely. So I may not try to implement DTO at such layer.

In the meantime, I found that some project may take out those Entity Model like the following diagram. Is it a good way to solve the problem?

                             +-------------------+
                             |                   |
          +------------------+   Presentation    |
          |                  |                   |
          |                  +---------+---------+
          |                            |
          |                            |
          |                            |
          |                            |
    +-----v------+           +---------v---------+
    |            |           |                   |
    |  Entities  <-----------+        BLL        |
    |            |  (Function that returning Entity Model)
    +-----^------+           +---------+---------+
          |                            |
          |                            |
          |                            |
          |                            |
          |                  +---------v---------+
          |                  |                   |
          +------------------+        DAL        |
                             |   (Repositories)  |
                             +-------------------+

Although this design can solve some of the problem, developers can attempt to update the entity to DB from Presentation Layer by calling entity.SaveChanges(). Which is so dangerous. How can I prevent other developer from calling SaveChanges() at the upper layer?

Best Answer

How can I prevent other developer from calling saveChanges() at the upper layer?

The only sane way to do that is to enforce a true separation between layers, and the only reasonable way you can do that is to maintain your own set of Data Transfer Objects (DTO's) at the Presentation/BLL interface, which defeats the purpose of using Entity Framework.

Let me show you something.

At my previous job, we used Entity Framework, but we never called SaveChanges() or used the DBContext object for anything except referring to the Entity classes. Instead, we used bare queries.

using (var context = new CustomerContext())
{
    var customer = context.Database.SqlQuery<Customer>(
          "SELECT * FROM Customers WHERE CustomerID = {0}", customerID).FirstOrDefault();
}

Over time, we began to forego the use of generated DTO's from EF, and just began creating our own DTO's. Bare queries do not require all the machinery that EF puts into a DTO, like change tracking, and you can customize each DTO for specific queries.

This sounds like we were taking on a lot of work that Entity Framework already does for you, but it comes with some significant benefits:

  1. Database queries are more specifically targeted to each task.
  2. Vastly simpler under the hood.
  3. Substantially better performance.

In my current job, I carried over this technique, but replaced Entity Framework with Dapper, the micro-ORM that Stack Exchange uses. The queries look almost exactly like the one above. I use this tool to generate entity classes.

The nice thing about Dapper is it contains CRUD extensions that include change tracking. You just call Update() on the affected entities. The difference is that now you can hide the Update method from your Presentation layer if you want to; just forego the IDbConnection object that Dapper uses.

Related Topic