aaroncarsonart
aaroncarsonart

Reputation: 1124

ASP.NET migrations is creating duplicate bridge entities

I am designing a web application with ASP.NET using MVC 5 and EF 6. Our team uses code first migrations to design our database. In our project we have two models - Location and Recreation. Recreation is essentially an enumerable tag that can be applied to any Location (many-to-many relationship), so I designed a 3rd bridge entity model to handle the relations (so I can reference it in the code). Here are the abbreviated model definitions:

Location:

/*
 * Represents a location on a map.
 */
public class Location
{
    public int LocationID { get; set; }
    public String Label { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public virtual ICollection<Recreation> Recreations { get; set; }
}

Recreation:

/*
 * Represents a recreation activity type, such as Hiking or Camping, that 
 * can be applied to any location as a tag (each Location may have 0 or more
 * Recreation tags).
 */
public class Recreation
{
    public int RecreationID { get; set; }
    public string Label { get; set; }

    public virtual ICollection<Location> Locations { get; set; }
}

Bridge Entity:

/*
 * Bridge entity to handle associations of Location and Recreation
 */
public class LocationRecreation
{
    [Key]
    [ForeignKey("Location")]
    [Column(Order = 1)] 
    [Display(Name = "Location")]
    public int LocationID { get; set; }

    [Key]
    [ForeignKey("Recreation")]
    [Column(Order = 2)]
    [Display(Name = "Recreation")]
    public int RecreationID { get; set; }

    public virtual Location Location { get; set; }
    public virtual Recreation Recreation { get; set; }
}

When I add a migration and run update-database, this works with the default scaffolding of controllers/views. But, upon looking in the Server Explorer tab, I see the following tables:

The views process CRUD on LocationRecreations, but the virtual properties point to RecreationLocations, so they don't work as that table remains empty.

What is causing the migrations to create a duplicate table? Can I modify my models in some way to allow for only one table to be created, and so that the virtual properties function as intended?

EDIT:

I have re-created the error in a new Visual Studio Project hosted here on Github. For clarity, I selected Individual User accounts, and thus am using the single ApplicationDbContext in the IdentityModels.cs file for my db context:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public DbSet<Location> Locations { get; set; }
    public DbSet<Recreation> Recreations { get; set; }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }


    // Associate a Location with a Recreation.
    public void AddOrUpdateRecreationLocation(string locationLabel, string recreationLabel)
    {
        var location = this.Locations.SingleOrDefault(l => l.Label == locationLabel);
        var recreation = location.Recreations.SingleOrDefault(r => r.Label == recreationLabel);

        //i if it does not exist, register the item.
        if (recreation == null) location.Recreations.Add(this.Recreations.Single(r => r.Label == recreationLabel));
    }
}

Upvotes: 0

Views: 589

Answers (1)

aaroncarsonart
aaroncarsonart

Reputation: 1124

I resolved my issue. Location and Relation both need a virtual ICollection<LocationRelation> (a collection of my bridge entity). The problem was I was telling the entity framework to point indirectly to the related items, and thus it inferred to create a third bridge table for me.

The LocationRecreation and DbContext code is fine. Here are the modified Location and Recreation models:

/*
 * Represents a location on a map.
 */
public class Location
{
    public int LocationID { get; set; }
    public String Label { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public virtual ICollection< LocationRecreation > LocRecs { get; set; }
}

/*
 * Represents a recreation activity type, such as Hiking or Camping, that 
 * can be applied to any location as a tag (each Location may have 0 or more
 * Recreation tags).
 */
public class Recreation
{
    public int RecreationID { get; set; }
    public string Label { get; set; }

    public virtual ICollection< LocationRecreation > LocationRecs { get; set; }
}

Upvotes: 1

Related Questions