2

I've spent the last three hours trying to figure this out and finally gave up (I'll work around it). But... just to be sure... is there no way to set up a unidirectional 0:1/1:1 in EF6 Fluent API?

Consider:

CREATE TABLE LegacyUsers (
    ID INT NOT NULL PRIMARY KEY,
    UserName NVARCHAR(50),
    EmployeeID INT NULL
)

CREATE TABLE Employees (
    ID INT NOT NULL PRIMARY KEY,
    EmployeeName NVARCHAR(50)
)

Domain Models:

public class LegacyUser {
    public int ID {get;set;}
    public int? EmployeeID {get;set;}
    public virtual Employee Employee {get;set;}
}

public class Employee {
    public int ID {get;set;}
    public string EmployeeName {get;set;}
}

"Fluent" (haha) API theoretical setup:

modelBuilder.Entity<LegacyUser>()
    .HasOptional(x => x.Employee)
    .WithForgeignKey(x => x.EmployeeID)

I've researched for hours and tried any number of ways to configure this. For my efforts I've been rewarded with "column name already exists" validation errors or "invalid column: Employee_ID" errors (of these which I could fix easily enough had this been bidirectional, but this is a more or less locked schema). The only thing that I've found that would force it to work is to try it as a 1:M relationship, which throws off the whole "fluency" by having to use the domain model property as a collection rather than a simple, single property.

Is there really no way to do this as easily as I seem to think there should be? The requirement is very simple: get the associated employee object given the employee ID on file for the legacy user (without having to mangle the models or add new fields to the database)

(for reference):

One to zero-or-one with HasForeignKey

Unidirectional One-To-One relationship in Entity Framework

Community
  • 1
  • 1
jleach
  • 7,410
  • 3
  • 33
  • 60

1 Answers1

2

The only thing that I've found that would force it to work is to try it as a 1:M relationship, which throws off the whole "fluency" by having to use the domain model property as a collection rather than a simple, single property.

This is indeed the only way to setup such relationship as described in Associations in EF Code First: Part 5 – One-to-One Foreign Key Associations. But you are misreading the unidirectional part. Luckily the "uni" part is the single navigation property at the dependent side - exactly where the foreign key property exists.

So your model is ok, you just need to set it up like this:

modelBuilder.Entity<LegacyUser>()
    .HasOptional(e => e.Employee)
    .WithMany()
    .HasForeignKey(e => e.EmployeeID)
    .WillCascadeOnDelete(false);

The essential part is the parameterless WithMany call.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Thanks, but unless I'm missing something, that still forces the domain property to be exposed as a collection rather than a single property. If there were a way I could expose that collection _only_ to the EF project but not to consuming projects, I could put a wrapper property around it to get FirstOrDefault() for "actual" domain consumption, but I don't think that can be done. I'll look around though. – jleach Oct 19 '16 at 17:53
  • What domain collection property are you talking about? I don't see such in the domain model, do you? – Ivan Stoev Oct 19 '16 at 17:55
  • In order to use `WithMany()`, my `public virtual Employee Employee {get;set;}` needs to be an `ICollection` type instead of a single Employee instance (unless I'm missing something... this was the block I hit when trying that approach earlier) – jleach Oct 19 '16 at 17:58
  • No, it doesn't. Don't touch your domain model classes, just use the fluent configuration from the answer. – Ivan Stoev Oct 19 '16 at 18:01
  • Well! I'm glad I asked. My pardons, I was indeed missing something - what that might have been I'm not sure, but running through it again as you describe does indeed work as I wanted. Thanks a bunch! – jleach Oct 19 '16 at 18:04
  • You are welcome. And no problem, it happens to all of us sometimes :) – Ivan Stoev Oct 19 '16 at 18:06