Andy Baker
Andy Baker

Reputation: 811

What am I doing wrong with my relationships? Common entity referenced by two dependent entities

I'm having trouble defining a foreign key relationship in entity framework. I've boiled the code down to the minimum to demonstrate the problem.

The model is defined as ;

    public class AccessControlList
{
    [Key] public int Id { get; set; }
    public string Name { get; set; }
}

public class ManagedFile
{
    [Key] public int Id { get; set; }
    public string Name { get; set; }
    public virtual AccessControlList AccessControl { get; set; }
}

public class ManagedFolder
{
    [Key] public int Id { get; set; }
    public string Name { get; set; }
    public virtual AccessControlList AccessControl { get; set; }
    public virtual ISet<ManagedFile> Files { get; private set; }

    public ManagedFolder()
    {
        Files = new HashSet<ManagedFile>();
    }
}

So essentially, I have a single AccessControl entity that can be referenced by either a ManagedFile or ManagedFolder entity.

To make sure the foreign key is added to ManagedFile and ManagedFolder, I've added the following fluent mapping

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<ManagedFile>()
                    .HasRequired(mf => mf.AccessControl)
                    .WithRequiredDependent()
                    .Map(m => m.MapKey("AccessControlListId"));


        modelBuilder.Entity<ManagedFolder>()
                    .HasRequired(mf => mf.AccessControl)
                    .WithRequiredDependent()
                    .Map(m => m.MapKey("AccessControlListId"));

        base.OnModelCreating(modelBuilder);
    }

This builds a database that looks exactly as I expect, with a foreign key AccessControlListId on each of ManagedFile and ManagedFolder.

Entity Framework Created Schema

It all looks good to me, but when I try to insert some data, with this code

using(var ctx = new MyContext())
{
    var mf = ctx.Folders.Add(new ManagedFolder {Name = "$/"});
    mf.AccessControl = new AccessControlList();

    var file = new ManagedFile {Name = "myfile.txt"};
    mf.Files.Add(file);
    file.AccessControl = new AccessControlList();

    ctx.SaveChanges();
}

It throws an error...

Error

I can't figure out what I've done that's incorrect?

As I understand my mapping, EF can save a new AccessControlList entity, then use the Id to write either the ManagedFolder or ManagedFile table.

As always, I appreciate any help offered.

Thanks Andy

Upvotes: 4

Views: 2481

Answers (1)

Slauma
Slauma

Reputation: 177133

Why does the database look "exactly as you expect"? Apparently you are trying to define two one-to-one relationships (.HasRequired(...).WithRequiredDependent(...)) but your relationships in the database are one-to-many.

Do you want one-to-one to one-to-many relationships? In the latter case (which is easier) just replace the two WithRequiredDependent() methods by WithMany().

If you want one-to-one you can't define a separate foreign key AccessControlListId. You must use shared primary keys, i.e. the PK of the dependent is the FK at the same time. The mapping would have to be:

modelBuilder.Entity<ManagedFile>()
            .HasRequired(mf => mf.AccessControl)
            .WithRequiredDependent();
// the same for ManagedFolder

...without the MapKey.

Upvotes: 5

Related Questions