Eliminating the need of using the virtual keyword in lazy loaded navigation properties by using IL weaving and the Fody plugin Virtuosity.
Eliminating the need of using the virtual keyword in lazy loaded navigation properties by using IL weaving and the Fody plugin Virtuosity.
Recently we've had the opportunity to work on a greenfield project to develop a modern and extensible business application. We invested quite a bit of time in the architecture of the app and agreed on following the DDD software development principles. If you're following the articles of the great Vladimir Khorikov, you probably know about the numerous drawbacks in Entity Framework (EF) when designing entities and value objects. Many times you need to pollute your domain with infrastructure code in order to get EF working. This blog series will look at some of these issues and will show you how to create a clean DDD solution.
In Entity Framework you can load related data by using lazy loading proxies. When accessing the navigation properties, lazy-loading will fetch the related entity in an extra database roundtrip. If you check the EF Core documentation, it says:
EF Core will enable lazy loading for any navigation property that can be overridden -- that is, it must be
virtual
and on a class that can be inherited from.
In the following example, the Posts
navigation property will be lazy loaded:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
However, the virtual
keyword is something we would like to get rid of, when designing a clean DDD domain entity. It is infrastructure code that doesn't belong in the domain.
With the help of IL Weaving
we can keep the domain model clean at design-time and weave the virtual keyword into our entities at build-time. After some quick research we came across the very helpful Fody plugin Virtuosity, which does exactly that. The are only two steps necessary:
<Virtuosity/>
to FodyWeavers.xml
In our case we configured the FodyWeavers.xml
to include only our domain entities in the Domain.Model namespace and below:
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Virtuosity>
<IncludeNamespaces>
OurProjectTitle.Domain.Model
OurProjectTitle.Domain.Model.*
</IncludeNamespaces>
</Virtuosity>
</Weavers>
Instead of polluting our domain entities with "unneccessary" (i.e. irrelevant for our business domain) virtual references we end up with a clean domain entity class, not carrying any infrastructure code:
public class Car
{
public Car(string name, Specs specs)
{
Name = name;
Specs = specs;
}
protected Car()
{
}
public CarHolder? CarHolder { get; protected set; }
public int Id { get; protected set; }
public string Name { get; protected set; }
public Specs Specs { get; protected set; }
}
And thanks to the Fody plugin, after the build we get this weaved result, so that EF Core can lazy-load the CarHolder
on demand:
public class Car
{
public Car(string name, Specs specs)
{
Name = name;
Specs = specs;
}
protected Car()
{
}
public virtual CarHolder? CarHolder { get; protected set; }
public int Id { get; protected set; }
public string Name { get; protected set; }
public Specs Specs { get; protected set; }
}
The part 2 of our series will take a closer look at the empty constructor we need to enable EF Core to instantiate the object and set the Specs
value object.