Page 10 - MSDN Magazine, April 2018
P. 10
Data Points JULIE LERMAN
EF Core 2 Owned Entities and Temporary Work-Arounds
The new Owned Entity feature in EF Core 2.0 replaces the Complex Type feature of Entity Framework “classic” (EF thru EF6). Owned Entities allow the mapping of value objects to the data store. It’s quite common to have a business rule that permits properties based on value objects to be null. Moreover, because value objects are immutable, it’s also important to be able to replace properties that contain a value object. The current version of EF Core doesn’t allow either of these scenarios by default, although both will be supported in upcoming iterations. In the meantime, rather than treating these limitations as showstoppers for those of us who love the benefits of domain-driven design (DDD) patterns, this article will show you how to work around those limitations. A DDD practitioner may still reject these temporary patterns as not following the principles of DDD closely enough, but the pragmatist in me is satisfied, but- tressed by the knowledge that they are simply temporary solutions.
Notice that I said “by default.” It turns out that there is a way to have EF Core take responsibility to enforce its own rule about null owned entities and allow for value object replacement, with- out dramatically affecting your domain classes or your business rules. In this column, I’ll show you how to do this.
Given that a value object is composed of its properties and then, as a whole, used as a property in another class, persisting its data takes some special effort.
There is another way around the nullability problem, which is to simply map the value object type to its own table, thus physi- cally splitting the value object data from the rest of the object to which it belongs. While this may be a good solution for some sce- narios, it’s generally one I don’t want to use. Therefore, I prefer to use my work-around, which is the focus of this column. But first
I want to ensure you understand why this problem and solution are important enough that I’m devoting this column to the topic.
A Short Primer on Value Objects
Value objects are a type that lets you encapsulate multiple values into a single property. A string is a great example of a value object. Strings are made up of a collection of chars. And they are immutable—immutability is a critical facet of a value object. The combination and order of the letters c, a and r have a specific mean- ing. If you were to mutate it, for example by changing the last letter to a “t,” you’d completely change the meaning. The object is defined by the combination of all of its values. As such, the fact that the object can’t be modified is part of its contract. And there’s another important aspect of a value object—it doesn’t have its own identity. It can be used only as a property of another class, just like a string. Value objects have other contractual rules, but these are the most important ones to start with if you’re new to the concept.
Given that a value object is composed of its properties and then, as a whole, used as a property in another class, persisting its data takes some special effort. With a non-relational database such as a document database, it’s easy to just store the graph of an object and its embedded value objects. But that’s not the case when storing into a relational database. Starting with the very first version, Entity Framework included the ComplexType, which knew how to map the properties of the property to the database where EF was persisting your data. A common value object example is PersonName, which might consist of a FirstName property and a LastName property. If you have a Contact type with a PersonName property, by default, EF Core will store the FirstName and LastName values as additional columns in the table to which Contact is mapped.
An Example of a Value Object in Use
I’ve found that looking at a variety of examples of value objects helped me to better understand the concept, so, I’ll use yet another example—a SalesOrder entity and a PostalAddress value object. An order typically includes both a shipping address and a billing address. While those addresses may exist for other purposes, within the context of the order, they’re an integral part of its definition. If a person moves to a new location, you still want to know where that order was shipped, so it makes sense to embed the addresses into the order. But in order to treat addresses consistently in my system, I prefer to encapsulate the values that make up an address in their own class, PostalAddress, as shown in Figure 1.
Code download available at msdn.com/magazine/0418magcode.
6 msdn magazine