This is a reply to Reference data as code: https://enterprisecraftsmanship.com/posts/reference-data-as-code
I recommend reading it first, because it's good and because otherwise this won't make sense to you.
TLDR; I strongly recommend replacing static readonly fields with creation methods or equivalent getters.
public class Industry : Entity
{
- public static readonly IndustryCars = new Industry(1, "Cars");
- public static readonly IndustryPharmacy = new Industry(2, "Pharmacy");
- public static readonly IndustryMedia = new Industry(3, "Media");
+ public static IndustryCars => new Industry(1, "Cars");
+ public static IndustryPharmacy => new Industry(2, "Pharmacy");
+ public static IndustryMedia => new Industry(3, "Media");
public string Name { get; private set; }
private Industry(int id, string name)
{
Id = id;
Name = name;
}
}
There are one or two reasons why I'm writing this.
The first is that you shouldn't rely on reference equality in client code. I am hesitating to accept perf improvements a good argument to keep them as static readonly.
var left = Industry.IndustryMedia;
var right = _industryRepository.GetByName("Media");
// Won't work anyway
// ❌ Assert.True(ReferenceEquals(left, right));
// Always do full Equals
Assert.Equals(left, right);
The second, the reason why I'm writing this, it's that it is common to have bidirectional navigation properties when using Entity Framework.
public class Customer : Entity
{
public Industry Industry { get; private set; }
}
public class Industry : Entity
{
public static readonly IndustryCars = new Industry(1, "Cars");
public static readonly IndustryPharmacy = new Industry(2, "Pharmacy");
public static readonly IndustryMedia = new Industry(3, "Media");
public string Name { get; private set; }
private Industry(int id, string name)
{
Id = id;
Name = name;
}
+ public ICollection<Customer> Customers { get; private set; }
}
In this situation, Industry.IndustryMedia.Customers
is a collection that is updated by Entity Framework whenever you add a new customer in the Media industry to the DbSet.
var customer = new Customer(Industry.IndustryMedia);
someDbContext.Customers.Attach(customer);
someDbContext.SaveChanges()
Assert.Contains(customer, Industry.IndustryMedia.Customers);
Because the Industry.Customers
collection navigation property is mutable shared state in this configuration, other threads can observe your new Customer even before the transaction is committed (after Attach()
). I found this leads to incorrect behavior that are a real PITA to debug.
Runnable demo https://dotnetfiddle.net/T0eHQJ