Reputation: 731
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
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