weblar83
weblar83

Reputation: 731

Entity Framework Many-to-Many Mapping

I'm moving across to Entity Framework as part of an ASP.Net MVC application I'm developing. Using code-first, I'm trying to get a simple many-to-many mapping working for a user role listing.

I have the following classes (and related database tables)

public class User 
{
    public int UserID { get; set; }
    public string EmailAddress { get; set; }
    // etc...

    public virtual ICollection<Role> Roles { get; set; }
}

public class Role
{
    public int RoleID { get; set; }
    public string RoleName { get; set; }

    public virtual ICollection<User> Users { get; set; }
}

public class UserRoles
{
    public int UserRoleID { get; set; }
    public int UserID { get; set; }
    public int RoleID { get; set; }
}

and an OnModelCreating override which maps the many-to-many relationships...

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .HasMany<Role>(u => u.Roles)
        .WithMany(r => r.Users)
        .Map(ur => {
            ur.MapLeftKey("UserID");
            ur.MapRightKey("RoleID");
            ur.ToTable("UserRoles");
        });
}

public DbSet<User> Users { get; set; }

When a user is authenticated, I make the currently authenticated User object available as part of a custom IPrincipal implementation.

public class MyPrincipal : IPrincipal
{ 
    // User attached to this custom IPrincipal
    public User User { get; set; }
}

And the authentication request stage which makes the User available in Global.asax

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    // Get the FormsAuthentication cookie
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

        // If the cookie exists
        if (authCookie != null)
        {
            // Get the authentication ticket
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            var identity = new GenericIdentity(authTicket.Name, "Forms");
            var principal = new WebStoresPrincipal(identity);

            // Get the userdata from the ticket
            string userData = ((FormsIdentity)(Context.User.Identity)).Ticket.UserData;

            // Deserialize the JSON data
            var serializer = new JavaScriptSerializer();
            principal.User = (User)serializer.Deserialize(userData, typeof(User));

            // Set the context user
            Context.User = principal;
        }

}

However - maybe this is where I'm not understanding correctly or missing the point altogether - when I try to access the Roles to which the User is assigned, it is an empty collection whereas I had expected the Entity Framework to have populated the Roles collection.

So, given I have the following data:

Roles
RoleID, Name
1, Administrator
2, Contributor
3, Proof Reader

Users
UserID, Name, etc
1, Bob Smith
2, Joe Bloggs

UserRoles
UserRoleID, UserID, RoleID
1, 1, 1
2, 2, 3

I would have expected that if I view the data returned for User 1, I would see the Roles collection contain a single entry for Administrator role.

Clearly, I missing something fundamental here but having trawled through tens of websites, nothing beyond setting up the many-to-many is explained.

Upvotes: 2

Views: 288

Answers (1)

Georg Patscheider
Georg Patscheider

Reputation: 9463

It looks to me like the User object you are deserializing from the ticket is not attached to the database. Then EF will not load the Roles from the Roles table into this object.

Try loading the User from the DbContext instead.

// after "Deserialize the JSON data"
var loadedUser = dbContext.Users.Find(principal.User.UserId);

This object should have a populated Roles collection.

Upvotes: 2

Related Questions