0

I'm trying to make an optional relationship between to entities, but I'm having trouble finding the correct syntax. Actually I need a 0..1 to 0..2 relationship, but I'm guessing that once I find the way to do the 0..1 to 0..1 that will be trivial.

Simplified what I have is this:

class Foo
{
    int Id { get; set; }
    //navigation property
    virtual Bar Bar { get; private set; }
}

class Bar
{
    int Id { get; set; }

    int? LeftFooId { get; set; }
    [ForeignKey("LeftFooId")]
    Foo LeftFoo { get; set; }

    int? RightFooId{ get; set; } 
    [ForeignKey("RightFooId")]
    Foo RightFoo { get; set; }
}

A Foo can be connected to zero or one Bar, never more. A Bar can have a LeftFoo and a RightFoo, or one or neither. The Bar property of Foo should be null if it is not referenced by a Bar, and it should contain the Bar that references it when it is referenced by a Bar.

With the code above the Bar references the Foo correctly, but EF gives the Foo table a Bar_Id and the Bar property is always null.

I've tried several different ways to setup these entity classes and I've used differenct Fluent API calls to make this work, but I don't get the wanted results.

Wuolennaj
  • 192
  • 2
  • 10

3 Answers3

1

Try this:

public class Foo
{
    public int Id { get; set; }
    public virtual Bar Bar { get; set; }
}

public class Bar
{
    public int Id {get; set;}
    public virtual Foo LeftFoo {get;set;}
    public virtual Foo RightFoo {get;set;}
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Bar>()
                .HasOptiona(b => b.LeftFoo)
                .WithOptionalDependent()
                .Map(k = k.MapKey("LeftFooId"));

    modelBuilder.Entity<Bar>()
                .HasOptional(b => b.RightFoo)
                .WithOptionalDependent()
                .Map(k => k.MapKey("RightFooId"));
    ...
}

Result

Table Screenshot

Edit for seeding

I would do the following, didn't test the code, but it should work:

var bars = new List<Bar>();
   bars.Add(new Bar());
   bars.Add(new Bar());
...//this means as many as you need/want
   bars.ForEach(b => context.Bars.AddOrUpdate(b));
context.SaveChanges();

var leftFoos = new List<Foo>();
   leftFoos.Add(new Foo());
   leftFoos.Add(new Foo());
...//this means as many as you need/want
   leftFoos.ForEach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();

var rightFoos = new List<Foo>();
   rightFoos.Add(new Foo());
   rightFoos.Add(new Foo());
...//this means as many as you need/want
   rightFoos.Foreach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();

int i=0;
foreach(var bar in bars)
{
   bar.LeftFoo = leftFoos.ElementAt(i);
   bar.RightFoo = rightFoos.ElementAt(i);
   i++;
}
context.SaveChanges();

For simplicity bars, leftFoos and rightFoos have the same number of elements.

SOfanatic
  • 5,523
  • 5
  • 36
  • 57
  • This gives me exactly what I already had. The FK's are there, but when I assign the LeftFoo and RightFoo, those Foo's Bar_Id remains null. I tried to remedy this by adding to OptionalDependent(f => f.Bar), but that gives me the error: Schema specified is not valid. Errors: (85,6) : error 0040: Type Bar_LeftFoo is not defined in namespace (Alias=Self). – Wuolennaj Jul 24 '13 at 07:51
  • Are you sure you are not doing something else? As I tested this locally and it works. How are you assigning `left` and `right` to bar? can you post that? – SOfanatic Jul 24 '13 at 11:44
  • What I'm currently doing in my Seed is making a bunch of Bar's with all fk's null then save changes, then I loop over all Bars, and I create a new foo for each empty leftfoo or rightfoo, leaving the Bar property null, then I do another Save. Then I assign the new foo to the bar (bar.leftfoo = foo). Then an update and a save. – Wuolennaj Jul 24 '13 at 14:20
0

I modified the code like this:

class Foo
{
    int Id { get; set; }
    int? BarId { get; set; }
    [ForeignKey("BarId")]
    virtual Bar Bar { get; set; }
}

class Bar
{
    int Id { get; set; }

    int? LeftFooId { get; set; }
    [ForeignKey("LeftFooId")]
    Foo LeftFoo { get; set; }

    int? RightFooId{ get; set; } 
    [ForeignKey("RightFooId")]
    Foo RightFoo { get; set; }
}

I may be wrong, but automatic assignment of a pair of FK's my be impossible, at least from the Foo to the Bar: How would EF know if it should assign the FK to the LeftFoo or the RightFoo. So for now I'm just doing both assignments manually:

Foo myFirstFoo = new Foo();
Foo mySecondFoo = new Foo();
Bar myBar = new Bar();
context.Add(myFirstFoo); context.Add(mySecondFoo); context.Add(myBar);
context.SaveChanges();
myFirstFoo.BarId = myBar.Id;
myBar.LeftFooId = myFirstFoo.Id;
mySecondFoo.BarId = myBar.Id;
myBar.RightFooId = mySecondFoo.Id;
context.Update(myFirstFoo); context.Update(mySecondFoo); context.Update(myBar);
context.SaveChanges();

This seems to work, but I do hope it can be done better.

Wuolennaj
  • 192
  • 2
  • 10
0

Try make your properties virtual and use POCO proxies (using Create instead of new ...). This would ensure the automatic update on the side properties. i.e when assigning a Foo to a Bar the POCO proxy will assign the corresponding Bar to the Foo but, ... and this should be the main problem: what do you expect if assign a Bar to a Foo? Which property do you want to be automatically assigned, left of right? Annotate the Bar property [InverseProperty("...")] to indicate this and think about you may need another property of type Bar in Foo for the other relationship. Hope this helps