On 31 May 2019, Julie Lerman, joined us in Milton Keynes to present a talk at our local user group, MK.NET.
For many .NET developers, Julie Lerman is up there as one of the most recognisable voices of .NET education. If you’ve learned about databases and specifically, Entity Framework, then there is a high probability that you will have read one of her many books, or seen one of her online courses.
Julie’s talk was another high-turnout event, with well over 100 guests joining us. I know that many in our meetup audience were absolutely thrilled to watch and meet Julie in person.
Brief summary of Julie’s talk
You can watch the video of the talk in the YouTube above, but I have attempted to extract a relatively brief synopsis, below.
The main theme of the talk was this:
When designing a system, eventually, you will need to persist your data, so you want Entity Framework to work well with your domain-first design. Recent versions of EF core have improved, by better understanding your domain models and not forcing compromises in their design.
Domain Driven Development
DDD is all about “focussing on the domain” and “forget about the database and database-persistence when designing”. It’s very much about sitting down with clients to understand and model the actual business, before even contemplating how to architect a system.
- Be clear about the common language and terminology that the business uses, as this will likely feed into the naming within the technical implementation.
- If you are new to DDD, you should be clear that “it’s not just how to map to your classes into the database”.
- Collaboration with the client is important throughout the lifecycle of the project.
Entities always have an Id.
A nice perspective: The “domain” is the problem you are solving, the “model” is the solution.
Entity Framework is a mapper, which can sit between your domain model and your database schema. The DbContext actually creates for you the data model which sits logically between those two. It could be said that it addresses the theory that “you should never be mapping directly from domain to data models”.
Quote from a slide: “EF Core makes it easier to keep your mappings out of the way of good domain design”. Compared to the original version of EFCore, recent versions are making it easier to just work with domain classes and not have to “fight” EF to make things work.
Useful CLI:
- (Build)
dotnet build
- (Validate DbContext - checks you’ve written domain model in a way that EF understands)
dotnet ef dbcontext info --startup-project ../console
- (Run Tests)
dotnet test
- (Build)
Scalar properties
- New to EF Core 3, you can encapsulate scalar properties with backing fields.
- When designing your domain models, often it is undesirable to expose a property publicly because you want to protect its value (i.e. you explicitly want to create a
private
property). - This will be a really big deal for some people, because it caused problems when using previous versions of EF Core, because it was so reliant on reflection and could not see private fields. To get around this, you would have had to expose properties using at least a public ‘getter’ (you would then typically ‘set’ the value using another method or constructor), e.g:
- When designing your domain models, often it is undesirable to expose a property publicly because you want to protect its value (i.e. you explicitly want to create a
public string CompanyName {get; private set;}
- With EF Core 3, even if reflection can’t find your property (because you have made it private), EF can still understand how to persist these backing fields. This means you can now describe fields like this:
private string _companyName;
public string CompanyName => _companyName;
Collections
- Also new to EF Core 3, you can Encapsulate collections.
- Just as with scalar properties, in previous versions of EF Core, we had problems working with collections. For example, you would have had to write code that looks like this:
Public List<Employee> Employees {get; private set;}
The complication with the above example is that the ‘private setter’ doesn’t provide protection against developers just adding items to the collection. i.e. the ‘private setter’ is only about creating a new instance of a list - once that list does is instantiated, as long as you have access to the ‘getter’, there is nothing to stop you calling
Employees.Add(xyz)
. There was no way around this - your team would have to adhere to a completely unenforceable rule, to always use a specifically written “AddEmployee” method (and not just use.Add()
).- Now, with EF Core 3, we can write that code like this:
private ICollection<Employee> _employees
public IEnumerable<Employee> Employees => _employees.ToList();
Value Objects
- Julie stressed that it was ok to “not really understand” what Value Objects are, as they’re difficult “to get” - but once you do understand them, they “are magical”.
- EF Core 1 didn’t have any “complex types” or any other way to do this. A Value Object is a kind of “super complex-type”. EF Core 2 introduced this new type, referring to them as “Owned Types”.
A value object:
- doesn’t have an identity key (unlike an Entity, so this is a big differentiator)
- does not exist on its own, it is always used as a property of an entity or another value type.
- a value type does still have an identity, but rather than a single key, the identity is a compound of other attributes (Julie gave the example of a “string” being a great example of a value object, where the order of individual characters represents its identity).
- the equality operator compares all of the properties (as a compound).
- includes GetHashCode which returns a hash of all properties (as a compound).
An example of a simple value object (transcribed from Julie’s slides) :
public class PersonFullName : IEquatable<PersonFullName>
{
// ------ properties set only via constructor ------
public PersonFullName (string first, string last)
{
First = first;
Last = last;
}
public string First {get; private set;}
public string Last {get; private set;}
// ------ behaviours I always want ------
public string FullName => First + " " + Last;
public string FullNameReverse => $"{Last}, {First}";
// ------ Override Equals, GetHashCode, overload ==, != ------
public override bool Equals (object obj)
{
var other=(PersonFullName)obj;
Return !(other is null) && First == other.First && Last == other.Last;
}
public override int GetHashCode () { . . . }
public static bool operator == . . .
public static bool operator != . . .
}
Owned Type
- In EF Core we have an object called “Owned Type”
- Its purpose is not just for EF to implement Value Objects …but they are one of the things you can use it for.
- In order to explicitly let EF know it is an Owned Type, we need to mention
OwnsOne
in the mappings, like this:-
Entity<TEntity>().OwnsOne(t=>t.property)
Straightforward 1-to-1
- EF versions 1 through 6 never knew how to work with 1-to-1 mappings. E.g. it never knew which was the master and which was the child, etc. Historically you had to:-
- share a primary key just to make EF happy.
- explicitly define the bi-direction navigation properties, even if you didn’t want them.
- An example of the older code might look like this (exampled transcribed from Julie’s slides):-
public class Principal
{
public int PrincipalId{get; set;}
public Dependent Dependent {get; set;}
}
public class Dependent
{
[Key]public int PrincipalId{get; set;}
public Principal Principal{get; set;}
}
modelbuilder.Entity<Principal>()
.HasOne(p => p.Dependent)
.WithPrincipalRequired(d=>d.Principal)
.HasForeignKey(d=>d.PrincipalId)
- Now, with EF Core, it “just understands” this type of relationship, meaning that our code can simply look like this instead:
public class Principal
{
public int PrincipalId{get; set;}
}
public class Dependent
{
public int DependentId {get; set;}
Public int PrincipleId {get; set;}
}
Mapping many-to-many
- It’s on the radar to develop EF to better support this …
- … but right now, the only way to map many-to-many is to manually create a joining entity.
- Another way to consider this, is that from a DDD perspective, you won’t be relying on EF to automatically manage the joins for you, which is probably a good thing.
Value conversions
- This relates to correctly mapping a .NET type to a SQL type.
- EF Core now has built-in converters that make life easier. It works by :
- Taking a .NET type that has no corresponding sql types
- It converts it into a .NET type that does have known sql mapping
- It then goes ahead with the actual conversion to a sql type.
- This can be expressed like this:-
Entity<TEntity>()
.Property(t=>t.property)
.HasConversion( . . . )
… where an example of how to express HasConversion
could look like this (the following example converts a C# color
type into a string, suitable for SQL ):-
Entity<UniformColors>()
(u => u.ShirtPrimary).HasConversion(c => c.Name, s => Color.FromName(s));
- Julie mentioned that there are built-in converters used to save things like
enums
and ‘time intervals’ to strings, etc.
Shadow properties
- Shadow properties are really useful for defining things that you don’t explicitly want to add to your Entity. This will be data that only the Context will be aware of. A classic example would be to record audit data, such as “last modified”.
You can register the additional fields like this:- * Express a shadow property in the modelBuilder, like this:
modelBuilder.Entity<Employee>().Property<DateTime>("Created");
modelBuilder.Entity<Employee>().Property<DateTime>("LastModified");
You can then trigger automatic updates like this (in the Entities Context class):-
public override int SaveChanges()
{
var timestamp = DateTime.UTCNow;
foreach (var entry in ChangeTracker.Entries()
.Where(e => e.Entity is Employee &&
(e.State == EntityState.Added || e.State == EntityState.Modified)
))
{
entry.Property("LastModified").CurrentValue = timestamp;
If (entry.State==EntityState.Added)
{
entry.Property ("Created").CurrentValue = timestamp;
}
}
}
Other mentions
- netstandard version numbering is no longer in sync with dotnet core. E.g. dotnet core 3.0 is netstandard 2.2. You can read more about version numbering here on twitter from Kathleen Dollard
- “Peacock” - a Visual Studio Colourisation Extension by John Papa - to make it easier to differentiate different instances of VSCode.
Thanks y’all
Thank you to Julie for taking the time and effort to join us! I hope you enjoyed our company and had a fab day at Bletchley Park!
- If you don’t already follow Julie Lerman, you can find her on Twitter @julielerman. You can support her by watching her content on PluralSight or grabbing a copy of one of her many books.
Thank you to our event sponsors; pizza and venues are not free and are not something that anyone in our community should take for granted - we have generous corporations who make this happen.
- VWFS - For providing a superb large auditorium. Incidentally, VWFS are frequently hiring, so do consider looking at their careers page if you’re in the market for change.
- Twilio - For providing food and drink, for providing the resource of a person’s time to organise things and for enabling our speaker to join us.
- Kinetic - I wanted to make a special mention, as although they didn’t sponsor this event directly, they do provide a home for our regular monthly meetup. This kind of sustained support keeps the group going.
Thanks to the event organisers, Shahid Iqbal and Layla Porter. It’s easy to understate the amount of time and effort that goes into organising community events.