EF Core optional ValueObject as identity

255 views Asked by At

I am using value objects as identities and would like to know how to best deal with nulls in EF core.

For example if I have an employee with an optional title (mr, mrs, etc):

public class Employee
  : Entity,
    IAggregateRoot
{
  public EmployeeTitleId TitleId { get; private set; }
  public EmployeeFirstName FirstName { get; private set; }
  public EmployeeSurname Surname { get; private set; }
   ...
}

I could check for nulls everywhere in my code e.g.

if (employee.TitleId == null) ...

or I could use a default e.g.

if (employee.TitleId.Equals(EmployeeTitleId.None)) ...

with the EmployeeTitleId implemented as follows:

public class EmployeeTitleId
  : Value<EmployeeTitleId>
{
  public static readonly EmployeeTitleId None = new EmployeeTitleId();

  protected EmployeeTitleId() { }

  public EmployeeTitleId(Guid value)
  {
    if (value == default)
      throw new ArgumentNullException(nameof(value), "Employee title id cannot be empty");

    Value = value;
  }

  public Guid Value { get; internal set; }

  public static implicit operator Guid(EmployeeTitleId self) => self.Value;

  public static implicit operator EmployeeTitleId(string value)
    => new EmployeeTitleId(Guid.Parse(value));

  public override string ToString() => Value.ToString();
}

I would prefer the second approach as it seems cleaner but I don't know if this is overkill. It would also require a bit more setup on the entity framework side e.g.:

builder.OwnsOne(_ => _.TitleId).Property(_ => _.Value)
 .HasColumnName("TitleId").HasConversion(v => v == default ? (Guid?) null : v, v => v ?? EmployeeTitleId.None);

Does this seem like a viable approach or should I just stick to checking for null? If so, is there a convention I could use in the entity type configurations so that I don't have to manually set each HasConversion?

1

There are 1 answers

3
Cristian Szpisjak On

There is another way to filter the results globally. You can configure this in the OnModelCreating like:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // code omitted for brevity

    modelBuilder.Entity<Employee>().HasQueryFilter(p => p.TitleId == null);
}