I'm using asp.net mvc 4, EF, codefirst to make a many to many relation to a users and roles system
the user model:
public class User
{
#region properties
[Key]
public Int32 Id { get; set; }
[Required]
public String UserName { get; set; }
public String Password { get; set; }
[Required]
public String Email { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastUpdate { get; set; }
public DateTime? LastLogin { get; set; }
[ForeignKey("RoleId")]
public virtual ICollection<Role> Roles { get; set; }
#endregion //properties
#region constructors
public User()
{
Roles = new HashSet<Role>();
LastUpdate = DateTime.Now;
CreationDate = DateTime.Now;
}
#endregion //constuctors
}
the role model:
public class Role
{
[Key]
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastUpdate { get; set; }
[ForeignKey("UserId")]
public virtual ICollection<User> Users { get; set; }
public Role()
{
Users = new HashSet<User>();
CreationDate = DateTime.Now;
LastUpdate = DateTime.Now;
}
}
the context:
public class UserManagementContext : Context, IContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public UserManagementContext() {
Database.SetInitializer<UserManagementContext>(null);
}
void IContext.Setup(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Role>().ToTable("Roles");
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(
m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
m.ToTable("UserRoles");
});
}
}
When the database tables are generated the tables users, roles and userroles are there. Then I make a record in users, one in roles and one in userroles to connect those. The userroles table has two columns RoleId and UserId.
Then I try to load the roles of a user like this:
public String[] GetRoles(String userName)
{
//var user = ConcreteContext.Users.Include("Roles").Where(u => u.UserName == userName).FirstOrDefault();
var users = ConcreteContext.Users.Include(u => u.Roles);
var user = users.FirstOrDefault();
var roles = from r in user.Roles
select r.Name;
return roles.ToArray();
}
But the line with var users = ConcreteContext.Users.Include(u => u.Roles); raises the next error:
System.Data.SqlClient.SqlException: Invalid object name 'dbo.RoleUsers'.
If I change de table name of UserRoles to RoleUsers when de database is created (by using m.ToTable(RoleUsers) ), I get a lot of different errors about wrong field names.
Anyone an idea what I'm missing here?
Thanks in advance,
Willem
Any reason why you have to use the Fluent API?
You can map Many-to-many like this with data attributes:
public class User
{
[InverseProperty( "Users" )]
public virtual ICollection<Role> Roles {get;set;}
}
public class Role
{
[InverseProperty( "Roles" )]
public virtual ICollection<User> Users {get;set;}
}
This will do what I needed:
public class User
{
#region properties
[Key]
public Int32 Id { get; set; }
[Required]
public String UserName { get; set; }
public String Password { get; set; }
[Required]
public String Email { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastUpdate { get; set; }
public DateTime? LastLogin { get; set; }
[InverseProperty("Users")]
public virtual ICollection<Role> Roles { get; set; }
#endregion //properties
#region constructors
public User()
{
LastUpdate = DateTime.Now;
CreationDate = DateTime.Now;
}
#endregion //constuctors
}
public class Role
{
[Key]
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastUpdate { get; set; }
[InverseProperty("Roles")]
public virtual ICollection<User> Users { get; set; }
public Role()
{
CreationDate = DateTime.Now;
LastUpdate = DateTime.Now;
}
}
public class UserManagementContext : Context, IContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public UserManagementContext() {
Database.SetInitializer<UserManagementContext>(null);
}
void IContext.Setup(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Role>().ToTable("Roles");
}
}
Related
[Table("User")]
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string UserName { get; set; }
//I should join with User.UserName and UserRoles.UserName
public List<UserRoles> UserRolesList { get; set; }
}
[Table("Role")]
public class Role
{
public int Id { get; set; }
public string RoleKey { get; set; }
public string Description { get; set; }
}
[Table("UserRoles")]
public class UserRoles
{
public int Id { get; set; }
public string UserName { get; set; }
public string RoleKey { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<UserRoles> UserRolesList { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
//Getting all users with roles
var usersWithRoles= _dbContext.Users.Include(b => b.UserRolesList).ToList();
I have 3 tables (User,Role and UserRoles), I want to joins User and UserRoles table with UserName column. But EntityFramework not allow doing this with out primary key column. What should I do?
Thanks.
I think you should design the UserRole table like below
[Table("UserRoles")]
public class UserRoles
{
public int Id { get; set; }
public int UserId { get; set; }
public int RoleId { get; set; }
public User User {get; set;}
public Role Role {get;set;}
}
Using thing you can now join with userId and get the user name from user table. Also can join with role table by roleId and can get role key.
I am new to Entity Framework and Asp.NET, and therefore, struggling with creating database relationships within the Entity Framework.
I have two SQLite tables (Ticket and User) and have setup my entity models as follows:
public class Users
{
[ForeignKey("id")]
public int id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public virtual ICollection<Tickets> Tickets { get; set; }
}
public class Tickets
{
public int id { get; set; }
public string summary { get; set; }
public string description { get; set; }
public string c_location { get; set; }
public string c_store_device { get; set; }
public string category { get; set; }
public DateTime? created_at { get; set; }
public DateTime? closed_at { get; set; }
public int priority { get; set; }
public int? assigned_to { get; set; }
public DateTime? due_at { get; set; }
public DateTime? updated_at { get; set; }
public string status { get; set; }
public virtual Users Users { get; set; }
}
I am trying to use Entity Framework 7 to export an IEnumerable<Tickets> that includes the User assigned to each Ticket.
I have tried to create my model relationship in MyDBContext as a single User can have multiple Tickets, and also has a foreign key associated in my Sqlite database (Tickets.assigned_to = User.id):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Users - > many Tickets
modelBuilder.Entity<Users>()
.HasMany(p => p.Tickets)
.WithOne(e => e.Users)
.HasForeignKey(p => p.assigned_to);
}
My result ends up with Ticket data being exported, but against every ticket I see a null value for User:
[{"id":10002,...,"Users":null}]
When I use .Include() within my Repository to include each User like this:
public IEnumerable<Tickets> GetAll()
{
return _db.Tickets.Include(t => t.Users).ToList();
}
It results in the error
HTTP Error 502.3 - Bad Gateway
The specified CGI application encountered an error and the server terminated the process.
What I'm trying to retrieve is data that looks like:
{"Ticket";[{"id":10002,..."status":"closed"}],"Users":[{"id":"1"..."email":"johndoe#someplace.com"}]}
I know it probably has something to do with my relationship model, but I cannot work out what I am doing wrong.
First you should really derive your Users from IdentityUser. It helps when trying to wire up the relationship, but I will give you the answer based on your current models. Your ForeignKey property should be on the child entity. By naming conventions, which is what EF uses by default, your public Users Users works better if you put a public int UsersId. Then essentially what EF will do is from your public Users Users it will go to the Users table. Then it looks for the ForeignKey which is set to Id, so now we are in the Users Table looking at the id property. Then it looks for the naming convention UsersId and if it sees it, it will set that property to the value that it saw from the Users Table Id column.
Try using this
public class Users
{
public int id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public virtual ICollection<Tickets> Tickets { get; set; }
}
public class Tickets
{
public int id { get; set; }
public string summary { get; set; }
public string description { get; set; }
public string c_location { get; set; }
public string c_store_device { get; set; }
public string category { get; set; }
public DateTime? created_at { get; set; }
public DateTime? closed_at { get; set; }
public int priority { get; set; }
public DateTime? due_at { get; set; }
public DateTime? updated_at { get; set; }
public string status { get; set; }
[ForeignKey("Id")]
public int UsersId { get; set; }
public virtual Users Users { get; set; }
}
and for your Fluent API configuring
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Users - > many Tickets
modelBuilder.Entity<Users>()
.HasMany(p => p.Tickets)
.WithOne();
}
Now all that does is create the relationship. In order to view the specific items you want to view, use a ViewModel. So, pull the two lists you want from where you want. Then use logic to separate the list how you want them to display.
public class UsersViewModel()
{
public UsersViewModel(Users user, List<Tickets> tickets)
{
this.first_name = user.first_name;
this.last_name = user.last_name;
this.email = user.email;
this.Tickets = new List<Tickets>();
foreach(var ticket in tickets)
{
if(ticket.UserId == user.Id)
{
this.Tickets.Add(ticket)
}
}
}
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public List<Tickets> Tickets { get; set;}
}
then in your controller make your list
public IActionResult Index()
{
var usersList = _repository.Users.ToList();
var ticketsList = _repository.Tickets.ToList();
var model = new List<UsersViewModel>();
foreach(var user in usersList)
{
var listItem = new UsersViewModel(user, ticketsList);
model.Add(listItem);
}
return View(model);
}
or use a Linq query
public IActionResult Index()
{
var usersList = _repository.Users.ToList();
var model = new List<UsersViewModel>();
foreach(var user in usersList)
{
var ticketsList = from x in _repository.Tickets where x.UserId.Equals(user.Id) select x;
var listItem = new UsersViewModel(user, ticketsList);
model.Add(listItem);
}
return View(model);
}
then at the top of your view you should have
#model IEnumerable<UsersViewModel>
How to map foreign keys from two different table to one table in fluent Api?
My two model is like
public class Customer
{
[Key]
public string Userid { get; set; }
public string PassWord { get; set; }
public bool premium { get; set; }
}
public class Roles
{
[Key]
public string Name { get; set; }
public string Description { get; set; }
}
And 3rd table which has primary key of above table as foreign key?
public class CustomerRoles
{
public string RoleName { get; set; }
public string UserId { get; set; }
}
How to map in Fluent Api?
public class Customer
{
[Key]
public string Userid { get; set; }
public string PassWord { get; set; }
public bool premium { get; set; }
public ICollection<CustomerRole> CustomerRoles { get; set; }
}
public class Role
{
[Key]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<CustomerRole> CustomerRoles { get; set; }
}
public class CustomerRole
{
public string RoleName { get; set; }
public string UserId { get; set; }
public Role Role { get; set; }
public Customer Customer { get; set; }
}
public class AppContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Customer>().HasMany(c => c.CustomerRoles).WithRequired(cr => cr.Customer);
modelBuilder.Entity<Role>().HasMany(r => r.CustomerRoles).WithRequired(cr => cr.Role);
modelBuilder.Entity<CustomerRole>().HasKey(cr => new { cr.RoleName, cr.UserId });
}
}
PS: Class name should not be plural, it can confuse with array property.
update how to use it
static void Main(string[] args)
{
using (var ctx = new AppContext())
{
Customer customer = new Customer { Userid = "A" };
ctx.Customers.Add(customer);
Role role1 = new Role { Name = "Role1" };
ctx.Roles.Add(role1);
Role role2 = new Role { Name = "Role2" };
ctx.Roles.Add(role2);
customer.CustomerRoles = new[]
{
new CustomerRole { Customer = customer, Role = role1 },
new CustomerRole { Customer = customer, Role = role2 },
};
ctx.SaveChanges();
}
}
I have a model as below :
public class BaseModel
{
public DateTime? CrDate { get; set; }
[ForeignKey("CrUser")]
public ApplicationUser UserCr { get; set; }
public string CrUser { get; set; }
public DateTime? MdDate { get; set; }
[ForeignKey("MdUser")]
public ApplicationUser UserMd { get; set; }
public string MdUser { get; set; }
public bool IsDeleted { get; set; }
public void LogWhatever()
{
this.CrDate = System.DateTime.Now;
this.CrUser = ?????
}
}
How can i get the logged userID from model to store in CrUser ?
regards.
Asp.net Identity expose IUser(IdentityUser) Context, once user logged in.
Context.User.Identity.GetUserId()
So I've got these 2 class and User coming from individual user accounts
public class User : IdentityUser
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
public virtual ICollection<Basket> Baskets { get; set; }
}
public class Basket
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Key]
public int BasketId { get; set; }
public string Name { get; set; }
public int Words { get; set; }
public virtual ICollection<User> Users { get; set; }
}
EF has created UserBaskets with 2 FK. I have items in my Basket class that I seeded.
My question is, how can I add row to my junction table in the controller? For example, a logged user click on a basket and return the Id...Now I've got
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Basket(int? basketid)
{
if (ModelState.IsValid)
{
var job = User.Identity.GetUserId();
job.Baskets.Add(basketid);
db.Users.Add(job);
db.SaveChanges();
return RedirectToAction("Basket");
}
return View(db.Baskets.ToList());
}
Thank you for any help.
I finally chose to add a junction table manually
public class UserBasket
{
[Key]
public int UserBasketId { get; set; }
public virtual User User { get; set; }
public virtual Basket Basket { get; set; }
public DateTime Date { get; set; }
}
and could add a row using identityUser like this
UserManager<User> userManager = new UserManager<User>(new UserStore<User>(db));
var user = userManager.FindById(User.Identity.GetUserId());
Basket basket = db.Basket.Find(id);
var userbasket = new UserBasket {User = user, Basket = basket, Date = DateTime.Now };
db.UserBasket.Add(userbasket);
db.SaveChanges();