Auto-populating a field in an EF Core entity

I am work­ing on the back end for a web-based SAAS appli­ca­tion, writ­ten in C#, and using .NET Core 2.1.  The data­base is most­ly han­dled by Entity Framework with cus­tom code that sup­ports the archi­tec­ture as a whole.

I had the sim­ple require­ment of pop­u­lat­ing a col­umn in our data­base with the “own­er” (or cre­ator) of the record.  The own­er is an appli­ca­tion user, and it is com­plete­ly depen­dent on the con­text of the cur­rent request. I want­ed to make this require­ment trans­par­ent to the rest of the application.

It should be not­ed at this point that there are mul­ti­ple ways to solve this prob­lem. The approach below is prob­a­bly more com­plex than most would find nec­es­sary. In fact, in our final imple­men­ta­tion, we will prob­a­bly rip a lot of this out and go with some­thing sim­pler. That said, I have found the explo­ration quite inter­est­ing. It has revealed a num­ber of lim­i­ta­tions and quirks of Entity Framework’s cur­rent implementation.

Limitations on an entity

The goal is to get a ref­er­ence to an ICurrentUserProvider into the enti­ties so they can update their fields appro­pri­ate­ly. The obvi­ous (and, as it turns out, some­what naïve) solu­tion is to inject an instance of the inter­face into the enti­ties. I will note again, that this is prob­a­bly not the best approach to solve this prob­lem, but I will car­ry on.

When cre­at­ing an enti­ty instance, Entity Framework sup­ports some lim­it­ed forms of depen­den­cy injec­tion. It will hap­pi­ly cre­ate an enti­ty that has a con­struc­tor that requires the DbContext, for exam­ple. It will not, how­ev­er, use the IServiceCollection of the appli­ca­tion to inject an arbi­trary ser­vice. I have seen notes that this fea­ture is com­ing, but as of this writ­ing, workarounds are necessary.

An attempt at a solution

The first thing I did was make my DbContext over­ride imple­ment the inter­face in ques­tion. To pre­vent pol­lu­tion of the name­space, I made it an explic­it imple­men­ta­tion. Something like this:

public class CustomDbContext : DbContext, ICurrentUserProvider
{
    private readonly ICurrentUserProvider _currentUserProvider;

    public CustomDbContext(DbContextOptions options, ICurrentUserProvider currentUserProvider) : base(options)
    {
        _currentUserProvider = currentUserProvider;
    }

    ICurrentUserProvider.CurrentUser => _currentUserProvider.CurrentUser;
}

In oth­er words, my CustomDbContext is pret­ty much just a wrap­per around the real CurrentUserProvider. It does not make any sense to put the respon­si­bil­i­ty in this class. A glar­ing issue here is that the class is going to fill up with oth­er­wise use­less inter­faces as the com­plex­i­ty of the sys­tem grows. But let’s move on.

Since the func­tion­al­i­ty we are imple­ment­ing here is com­mon through­out all of the enti­ties in the sys­tem (or most of them, at least), I put it into a base class, BaseEntity. The sim­pli­fied for this dis­cus­sion ver­sion follows:

public abstract class BaseEntity
{
    protected ICurrentUserProvider CurrentUserProvider { get; }

    public BaseEntity(ICurrentUserProvider currentUserProvider)
    {
         CurrentUserProvider = currentUserProvider;
         ModifiedBy = CreatedBy = currentUserProvider.CurrentUser.UserName;
    }

    public string ModifiedBy { get; set; }
    public string CreatedBy { get; private set; }
}

Now things start to get kind of cum­ber­some, in my opin­ion. Classes derived from this BaseEntity class can’t get an ICurrentUserProvider from Entity Framework when an enti­ty is con­struct­ed from the data­base. An abstract DbContext (think IDbContext) won’t work, either. For test­ing and oth­er pur­pos­es, we don’t want an entire con­crete instance of CustomDbContext, that would force every­thing to be inte­gra­tion tests!

So, derived class­es now have two con­struc­tors. As an example:

public class User : BaseEntity
{
    [UsedImplicitly]
    private User(CustomDbContext context) : base(context)
    {
    }

    public User(ICurrentUserProvider currentUserProvider) : base(currentUserProvider)
    {
    }

    public Guid Uuid { get; set; }
    [MaxLength(100)]
    public string Login { get; set; }
    [MaxLength(512)]
    public byte[] HashedPassword { get; set; }
    [MaxLength(128)]
    public byte[] PasswordSalt { get; set; }
    public virtual UserProfile UserProfile { get; set; }
}

I know, right? An implic­it­ly called pri­vate con­struc­tor just so EF can do its thing, and a pub­lic one that’s what we real­ly want so that we aren’t too con­strained when we test. We can’t even ask for the abstract DbContext here, unless we want to cast it to the inter­face. If we’re doing that any­way, we may as well be explic­it. Now imag­ine putting that pat­tern in every mod­el class.

At this point, I played around with the idea of just inject­ing the IServiceCollection into my con­text and let­ting the mod­els get what­ev­er ser­vices it need­ed from there. It may have made the con­cept eas­i­er to grasp, but it does­n’t remove the need for the dou­ble con­struc­tor pat­tern, and it com­pli­cates the unit tests

The only sav­ing grace for this pat­tern was that I did­n’t have to think much about the unit test setups. I could just inject a mock of my inter­face into the enti­ty, and go. But there is a bet­ter solu­tion, in a future post.

Share