0

I have 3 entities; Groups, Scopes, Vlans

What I'm trying to accomplish:

  • A Group can have many Scopes
  • A Group can have many Vlans
  • A Scope must have a Group
  • A Vlan must have a Group
  • A Scope can have a Vlan
  • A Vlan can have a Scope

So kind of a soft relationship between vlans and scopes.

When not setting vlan or scope as a requirement EF complains about not knowing the principal of the relationship, So how do I fix this soft one to one relationship?

My Models:

public class Group
{
    public Group()
    {
        Scopes = new HashSet<Scope>();
        Vlans = new HashSet<VLAN>();
    }
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public ICollection<VLAN> Vlans { get; set; } 
    public ICollection<Scope> Scopes { get; set; } 
}
public class Scope
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }     
    public virtual VLAN Vlan { get; set; }
    public virtual Group Group { get; set; }
}
public class VLAN
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public virtual Scope Scope { get; set; }
    [Required]
    public virtual Group Group { get; set; }
}
VisualBean
  • 4,908
  • 2
  • 28
  • 57
  • Isn't it the same problem there? http://stackoverflow.com/questions/6531671/what-does-principal-end-of-an-association-means-in-11-relationship-in-entity-fr – rnort Jun 02 '16 at 11:38
  • No, I don't want vlan to be dependant on Scope nor the other way around. but they do need to reference each other – VisualBean Jun 02 '16 at 11:40
  • Try defining an explicit FK that is nullable in each (public int? VLANId, public int? ScopeId) and set ForeignKey attribute on nav properties. This is generally better suited to fluent api: http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx – Steve Greene Jun 02 '16 at 13:38

1 Answers1

1

There is a similar problem on SO, related to bidirectional Zero-to-One relationships: Implementing Zero Or One to Zero Or One relationship in EF Code first by Fluent API

I've tried the same approach on your schema and it works. See my FluentAPI configuration below:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<VLAN>()
            .HasOptional(vlan => vlan.Scope)
            .WithOptionalPrincipal();

        modelBuilder.Entity<VLAN>()
            .HasRequired(vlan => vlan.Group);

        modelBuilder.Entity<Scope>()
            .HasOptional(scope => scope.Vlan)
            .WithOptionalPrincipal();

        modelBuilder.Entity<Scope>()
            .HasRequired(scope => scope.Group);

        modelBuilder.Entity<Group>()
            .HasMany(group => group.Scopes);
        modelBuilder.Entity<Group>()
            .HasMany(group => group.Vlans);

}

To be sure, that it suites your needs, see the generated SQL for VLANs and Scopes tables:

CREATE TABLE [dbo].[Scopes] (
[Id]       INT            IDENTITY (1, 1) NOT NULL,
[VLAN_Id]  INT            NULL,
[Group_Id] INT            NOT NULL,
CONSTRAINT [PK_dbo.Scopes] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.Scopes_dbo.VLANs_VLAN_Id] FOREIGN KEY ([VLAN_Id]) REFERENCES [dbo].[VLANs] ([Id]),
CONSTRAINT [FK_dbo.Scopes_dbo.Groups_Group_Id] FOREIGN KEY ([Group_Id]) REFERENCES [dbo].[Groups] ([Id]) ON DELETE CASCADE);

CREATE TABLE [dbo].[VLANs] (
[Id]       INT            IDENTITY (1, 1) NOT NULL,
[Scope_Id] INT            NULL,
[Group_Id] INT            NOT NULL,
CONSTRAINT [PK_dbo.VLANs] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.VLANs_dbo.Scopes_Scope_Id] FOREIGN KEY ([Scope_Id]) REFERENCES [dbo].[Scopes] ([Id]),
CONSTRAINT [FK_dbo.VLANs_dbo.Groups_Group_Id] FOREIGN KEY ([Group_Id]) REFERENCES [dbo].[Groups] ([Id]) ON DELETE CASCADE);
Community
  • 1
  • 1
rnort
  • 107
  • 1
  • 4