In a previous article titled "5 Aspects of Effective Persistence of Entity Framework Models," I assumed you were using Entity Framework for some of your data persistence work. In that article, I focus on a few technical issues and glitches you may encounter and, in the end, I didn’t question the use of Entity Framework in the persistence layer of a system. In this article, I just want to step back and show that Entity Framework—and Object/Relational Mapper (O/RM) frameworks in general—are fundamental tools for today’s applications design and implementation.
Related: The Entity Framework in Action
There are two ways to look at Entity Framework. One is to look at Entity Framework as a plain replacement for ADO.NET and raw SQL code. The other way to look at Entity Framework goes deeper and presents Entity Framework as some of the necessary plumbing for an application design centered on the true internal mechanics of a business domain. Yes, this is just what the popular acronym DDD, short for Domain-Driven Design, is all about.
From DDD to Entity Framework
Unless you have spent years caged in a dark and disconnected cave or in the middle of a rainforest, at the very minimum, you should have heard about DDD. What is it all about? Regardless of what most short executive summaries show, DDD is just what its expanded name says: design of a software system driven by the actual business domain. So what’s really new? Isn't this just the way we’ve always been supposed to write software? You talk to end users, collect requirements, arrange a model, and build the application. In which way is DDD different? And what’s the role of Entity Framework in this context?
The strategy above never fully worked. The strategy is clear, effective, and well-defined. But its implementation rarely produced software within expected time and budget. As I see it, the traditional strategy didn't work for two main reasons:
- Problems understandings requirements
- Designing the model
DDD offers a comprehensive approach to address both problems. I recommend a look at Chapter 5 of my book "Microsoft .NET: Architecting Applications for the Enterprise," 2nd Ed., Microsoft Press (2014), for a deep look into what DDD really is and how it addresses both problems in detail.
In short, DDD suggests you invest a lot of time understanding the mechanics of the domain, the language used by domain experts, and actual processes. As an architect or developer, you don’t have to change processes—that’s not your job. You have to replicate process and, in doing so, implement the most efficient algorithms. DDD is not strictly related to code, although its final destination is just having you produce a working software system. DDD is mostly about modeling. A landmark of DDD is the concept of a bounded context. A bounded context is a segment of the system that stands alone and can be designed and implemented in isolation using the technologies that best fit. In a system designed following DDD guidelines, each bounded context is planned using the technologies and approaches that best fit the skills and attitude of developers. For example, imagine a booking website.
The site will have a front end dedicated to actual users where they see allowable slots and resources and make their choice and confirm. In addition, there will likely be another bounded context where administrators of the site enter rules and settings. The admin bounded context has nearly no business logic or, at most, it has business logic related to plain CRUD operations—things like cascading rules, foreign-key relationships, and various data constraints. In most cases, it' stuff you can easily control at the database level. And the database can easily be a plain classic relational database.
The core of the site, instead, has more sophisticated logic. In addition, its logic that can be little understood, misunderstood, or just change frequently. A design that raises the level of abstraction from data to objects is probably more suitable and maintainable than a fixed schema of tables. Separation between contexts is key—in this way, the complexity of each context is kept to a minimum. Entity Framework is a plain tool that takes an object model and saves it to a relational database. By using Entity Framework, and exploring its potential, you can start a learning curve that evolves you from the level of a plain database developer to architect. Let’s look at the top five reasons why Entity Framework can make you a better and more powerful developer.
1. Shield Yourself From SQL
SQL happens. As a database developer, you probably built a career on your SQL skills. Quite likely, nothing in SQL really scares you so why on earth should you be moving away from SQL to embrace Entity Framework? I think it’s a general point that may not apply to any individual developer, but still remains valid as a global strategy. Overall, I believe it's a matter of expressivity, productivity, and maintainability. In addition, it's also a safer and more reliable option for all those who don’t manage the SQL language that well; or that simply don’t feel totally comfortable with it.
Any developer is probably good at writing a SELECT * FROM table query. It seems quite a trivial query, but some developers still miss that, performance-wise, it's preferable to list columns explicitly. Not a decisive factor to call the application slow, but a good example of the little things that an O/RM can do for you. You just use a higher-level language to indicate what you want and the O/RM generates the best SQL code at no additional cost for you. As an example, consider the following query written with LINQ-to-Entities, the Entity Framework query language that generates SQL under the hood.
where o.Customer.CustomerID == "ALFKI" &&
ID = o.OrderID,
Count = o.Order_Details.Count,
City = o.ShipCity
The query returns a projection of all orders placed by a given customer that have more than five items. It’s SQL in the end; and any good SQL developer can write a query for that. Using LINQ-to-Entities, though, it's far easier to read, understand, and modify for everyone. Even for SQL experts, this query reads more quickly than written with SQL.
2. Overall Simplification of Queries
The point of simplified queries is more general. When using Entity Framework or any other modern O/RM framework, you write your queries using a LINQ dialect. LINQ is a language baked by .NET compilers that offers a generic SQL-like language. You can express the projection of data you want, express WHERE and ORDERBY clauses and use JOIN and GROUP facilities. The great benefit is that the syntax is simplified compared to the raw syntax of SQL.
LINQ makes it suitable for many more developers to write effective queries. In a software application heavily based on manually optimized SQL code, the quality of the SQL code soon becomes a bottleneck. Not only can the SQL code can be suboptimal, but writing any SQL code that returns the desired data can be a time-consuming operation. Today, most O/RMs offer a LINQ provider and their own dialect of LINQ tailor-made for supported databases. The LINQ provider you find in Entity Framework is top quality.
3. Move Away From Data Model
Strictly related to the previous point is the fact that Entity Framework raises the level of abstraction at which you work. Speaking in general, any O/RM tool by contract should be capable of taking an object model and persist it to a relational schema of tables. This brings you to focus your programming efforts on objects rather than tables and database settings. Entity Framework is no exception.
Is there really any advantage in using objects instead of ADO.NET data readers? Frankly, I’m not sure there is any benefit from Entity Framework. The point is that the use of an O/RM implies the use of a (much) more conceptual data model—a domain-driven object model instead of a plain data model. Entity Framework offers two main approaches to get an object model to carry on queries and updates—database-first and code-first.
The database-first approach consists of a Visual Studio wizard that infers an object model from an existing database. The inferred model is nearly 1:1 with columns and tables in the specified database. The resulting object model is quite anemic in the sense that all classes have properties, but no methods. Visual Studio, however, implements the object model via .NET partial classes. This means that by writing your own extensions to those classes, you can add method to objects implementing business logic. You can have, for example, an OrderDetail class that knows how to calculate the total of the line.
A similar example is right and wrong at the same time. It's right because it shows an example of business logic coded into an object model class. It's wrong because it doesn’t show the real point of moving away from a database model and towards an object-oriented domain model. The benefit of moving away from a database model is that you start using objects; this is only the first step towards a better and more conceptual design of the system, or just certain parts of the system.
4. Neater Separation of Concerns
So you have a relational model that serves well the persistence needs of the application. Where would you put the business logic and application logic? In general, application logic is the implementation of use-cases offered by the user interface. Business logic is the logic of the entities in the business domain—orders, invoices, customers and any other more sophisticated entities you may have. Using an object model is really powerful if you have business logic in the model and use Entity Framework to persist the model. Otherwise, it's just using Entity Framework instead of ADO.NET and plain SQL. As mentioned, using Entity Framework as a replacement of SQL is still beneficial in terms of expressivity, but in doing so you lose a lot of potential along the way.
Modeling the business domain through objects leads to a much neater separation of concerns. The implementation of use-cases belongs to the application layer and is directly invoked from the user interface. Application layer orchestrates business logic through classes in the model and related services. Finally, no class in the model is really concerned about persistence as this is the realm of yet another layer of code—the persistence layer made of repository classes—that just use Entity Framework (or another O/RM framework) to save and read back classes.
5. Code-First as the Final Step
Entity Framework development can happen in either of two ways: (1) building an object model from an existing database; or (2) building a database from a newly created object model. This latter approach is known as Code-First. A Code-First model is centered on a class that inherits from DbContext, as in Listing 1.
public MyDatabase() : base("your-connectiong-string-entry")
Products = base.Set
Customers = base.Set
Orders = base.Set
protected override void OnModelCreating(DbModelBuilder modelBuilder)
The string passed to the constructor is the name of the database or the name of an entry in the configuration file indicating where to read details about the connection string and the data provider. The root class—MyDatabase in the example—has three collections that will map to tables: orders, customers and products. All you do is define Order, Customer and Product classes with properties and methods that try to explain how those objects behave in the business domain. The OnModelCreating method defines settings for the framework to create the actual database behind the model. Here’s an example that sets up a couple of columns in the Orders table.
.HasKey(o => o.OrderId)
.Property(o => o.OrderDate).IsRequired()
.Property(o => o.Description).HasMaxLength(100);
The OrderId property is going to be the primary key; the OrderDate property is a required column and Description column is not allowed more than 100 characters. In addition, Order has a one-to-many relationship to OrderDetails.
In the end, the purpose of Code-First is putting code on top of everything: You design your object model and Entity Framework takes care of the underlying database as well as persistence. Code-First is the final step on the way using Entity Framework in place of raw SQL and ADO.NET code.
A common objection to widespread use of Entity Framework for data access code is that all you need is CRUD. And when all you need is CRUD, plain SQL and, maybe stored procedures, is more than fine. In this article, I just tried to explain one key point: Moving away from a database-first mentality mostly means raising the abstraction level. It doesn’t mean forgetting core principles.
If you learn about DDD, you find out about "aggregates" and may find it obscure to identify them in a model. But then, aggregates are to objects the same that root tables are to relational databases. If OrderDetails depends on Orders, then you have an aggregate,and Orders is the root. It’s a different way of modeling, but it’s the same principles. Similarly, stored procedures existed for years to store small pieces of business logic related to database tables. Method in the domain model objects play exactly the same role. Using Entity Framework for a CRUD is just the first step towards using higher-level tools suitable for more complex application scenarios.