Optional One to One Relationships in EFCore

My company is currently working on replacing our venerable WinForms application with a shinier ASP.Net MVC version and rather than just re-write everything as a slavish “lift and shift” approach, we’ve gone for the bolder approach of completely re-designing the whole thing from top to bottom. Cue buzzwords such as “Greenfield” and “Blue-sky thinking” etc etc.

As part of this, we made an early decision to move to an Entity Framework Code First approach and try to keep as close to the cutting edge of technology as possible, so whilst we started with EF Core 3.1, we’re currently on EF Core 5. As someone who has been mucking about with SQL Databases since the 90s, it’s hard to let go of the SQL and just let EF manage and control everything, but once you embrace the mindset and just let it go, the benefits have been substantial.

However, one thing that has been niggling at me is managing 1:1 relationships and especially optional ones. It wasn’t long before we designed our first 1:1 relationships and instantly hit some problems.

Let’s look at the below diagram. Here we have Requirements which have an optional 1:1 (aka zero or one) relationship with a set of Features. When a Product is created from these requirements, it will also have a set of Features, nothing complicated here, all we’re doing is putting the set of common fields in a separate table, if we happen to have them.

Sample ER Diagram

So the first thing we did here was try to add a simple navigation property to Requirements, Products and Features and hoped EFCore could work this out by magic; something like:

public virtual FeatureEntity Features { get; set; }

Your migration will generate fine but as soon as you try to apply it and code against it, you’ll see this fun error:

The dependent side could not be determined for the one-to-one relationship between ….

Which makes perfect sense if you think about it, how on earth should we expect EF to work this out? Don’t get me wrong here, EF is about as close to computer witchcraft as I’ve seen in a long time, but you’ve got to give it a helping hand in places. Unfortunately this error leads you down the path of trying to use the fluent API to try and configure the relationship by hand, but I very much disagree here, the fluent API should only be used when you can’t express what you’re trying to achieve via Data Annotations.

What we actually want to do here is configure the foreign key properties in the dependant table. So in our FeatureEntity we add our navigation properties to Requirement and Product but we additionally add RequirementId and ProductId.

Because I’m following a naming convention of {TableName}Id, EF can work everything else out itself. No need for a ForreignKey data annotation.

[Table("Features")]
public class FeatureEntity
{
    [Key]
    public Guid Id { get; set; }

    public Guid? RequirementId { get; set; }
    public Guid? ProductId { get; set; }

    public virtual RequirementEntity Requirement { get; set; }
    public virtual ProductEntity Product { get; set; }

}

After this all you have to do is add your Navigation properties to Requirement and Product (nothing extra required here) and EF will magic together the rest for you.

The final point of note here is the optionality of the relationship. As you can see above, I’ve declared that both of these FKs are optional by using ? after the type. It’s as simple as that.

If you review your migration and ContextModelSnapshot, you’ll see that the FKs will all be unique and indexed and EF has worked out the details for you using the HasOne()/WithOne()/HasForeignKey() structure for you.

No custom OnModelCreating, No Fluent API. Simples.

Hello World

“Hello World!”, you’ll often see this chirpy piece of text in relation to computer programming and I think it’s highly appropriate that my first post on this blog is in keeping with that tradition, but where does this come from?

Before I started to write this post I thought I knew the answer with a laser-like certainty, but as is usually the case in these situations, when we’re at our most certain, we’re also at our most mistaken. Though the exact origins of Hello World remain lost in the mists of time, its use as a test phrase is widely believed to have begun with Brian Kernigham’s 1972 book, A Tutorial Introduction to the Language B. In which, the first known version of the program was used to illustrate external variables.

But I, as many of you was introduced to this expression back when I, albeit unknowingly, started on my journey to becoming a software developer. I can’t remember the exact year but I was definitely under the age of 10 when my mother suggested I might want a computer as my Christmas present. I wasn’t sure about this and I suspect I was persuaded into the idea. I’ll have to check with my mum here to get the facts, but this is how I remember it.

Roll on Christmas day, and I was confronted with my very first computer, a Sinclair ZX Spectrum as shown in all of it’s glory above. This was the very cutting edge of what would become Personal Computers and I won’t bore you with the history or details, beyond knowing that in order to even do anything with a Spectrum, you had to start typing code. Yup that’s right, if you ever owned a Spectrum, an Amstrad, Commodore or BBC Micro, you were basically being groomed into a career as a software developer without even knowing it.

Start screen of the ZX Spectrum
Touch any key and you’re instantly transported to a command line
Within minutes you’re writing your first program

I imagine that confronted with such a thing, you would be turning straight to the manual, and this is where the magic starts. After some extensive blurb about connecting up the myriad of wires, the first thing your manual tells you to do is get your new friend to say “Hello”.

Again, I’m surprised here, I would have bet a large portion of my fortune that the first thing I typed was “Hello World”, but having dutifully tracked down the very same manual on the internet, it turns out the first program I ever wrote was not “Hello World”, but in actual fact it was “Hello”.

The rest as they say is history: you quickly learned how to Load/Run games from cassette and that’s where most left it. Yet the more inquisitive in our midst would have absorbed that manual and been writing their own programs in short order and that is essentially the story of how I wrote my first program and set my feet upon the path of becoming a software developer.

These days, “Hello World” (See? I do get back to the point eventually) is used as the standard first piece of code written in computer languages, in fact there was a strong trend for a language to be judged on how simple it was to send this innocent and chirpy piece of text to the screen. Funnily enough, there are very few languages that can match the BASIC of the 80s for simplicity of this code.

I’ll leave you with what was probably my second program: