New to Entity Framework, trying to figure out some stuff. I have tables as listed below:
Users
---------
UserID
UserName
Roles
---------
RoleID
RoleName
UserRoles
---------
UserRoleID
UserID
RoleID
I am using a repository pattern. here's an example of a repository (they're all essentially the same)
public class RoleRepository : IRoleRepository
{
private AuthorizationContext context = new AuthorizationContext();
public IQueryable<Role> Roles
{
get
{
return this.context.Roles;
}
}
public bool Save(Role pRole)
{
if (pRole.RoleID == 0 || pRole.RoleID == null)
{
context.Roles.Add(pRole);
}
context.SaveChanges();
return true;
}
public bool Delete(Role pRole)
{
context.Roles.Remove(pRole);
context.SaveChanges();
return true;
}
}
Now, I want to test to see if a user (by UserName) belongs to a role (by RoleName). How can I query? I expected it to be something like this, but it doesn't work:
public bool IsUserInRole(string username, string roleName)
{
var repo = new UserRepository();
var user = repo.Users.FirstOrDefault(u => u.NetID == username && u.UserRoles.FirstOrDefault(r => r.Role.Name == roleName));
}
How can I query to see if the user belongs to the role? I would prefer to use predicates if possible.
I would use the .Any function like this:
public static bool IsUserInRole(string username, string roleName)
{
using(var roleRepository = new RoleRepository())
{
return roleRepository.Roles.Any(r => r.RoleName == roleName && r.UserRoles.Any(ur => ur.User.UserName == username));
}
}
Here is a sample Console Application:
class Program
{
static void Main(string[] args)
{
var users = new List<string> { "A", "B", "C", "D" };
var roles = new List<string> { "User", "Admin", "Superuser"};
//User A has roles: User, Admin, Superuser
Debug.Assert(IsUserInRole(users[0], roles[0]) == true);
Debug.Assert(IsUserInRole(users[0], roles[1]) == true);
Debug.Assert(IsUserInRole(users[0], roles[2]) == true);
//User B has roles: User, Admin
Debug.Assert(IsUserInRole(users[1], roles[0]) == true);
Debug.Assert(IsUserInRole(users[1], roles[1]) == true);
Debug.Assert(IsUserInRole(users[1], roles[2]) == false);
//User C has roles: User
Debug.Assert(IsUserInRole(users[2], roles[0]) == true);
Debug.Assert(IsUserInRole(users[2], roles[1]) == false);
Debug.Assert(IsUserInRole(users[2], roles[2]) == false);
//User D has no roles
Debug.Assert(IsUserInRole(users[3], roles[0]) == false);
Debug.Assert(IsUserInRole(users[3], roles[1]) == false);
Debug.Assert(IsUserInRole(users[3], roles[2]) == false);
Debugger.Break();
}
public static bool IsUserInRole(string username, string roleName)
{
using(var roleRepository = new RoleRepository())
{
return roleRepository.Roles.Any(r => r.RoleName == roleName && r.UserRoles.Any(ur => ur.User.UserName == username));
}
}
}
public interface IRoleRepository : IDisposable
{
}
public class RoleRepository : IRoleRepository
{
private Context context = new Context();
public IQueryable<Role> Roles
{
get
{
return this.context.Roles.AsQueryable<Role>();
}
}
public void Dispose()
{
//Do nothing
}
}
public class Context : IDisposable
{
public IList<User> Users { get; set; }
public IList<Role> Roles { get; set; }
public IList<UserRole> UserRoles { get; set; }
public Context()
{
//Generate Some Fake Data
Users = new List<User>();
Users.Add(new User { UserID = 1, UserName = "A" });
Users.Add(new User { UserID = 2, UserName = "B" });
Users.Add(new User { UserID = 3, UserName = "C" });
Users.Add(new User { UserID = 4, UserName = "D" });
Roles = new List<Role>();
Roles.Add(new Role { RoleID = 1, RoleName = "User" });
Roles.Add(new Role { RoleID = 2, RoleName = "Admin" });
Roles.Add(new Role { RoleID = 3, RoleName = "Superuser" });
UserRoles = new List<UserRole>();
UserRoles.Add(new UserRole(1, Users[0], Roles[0]));
UserRoles.Add(new UserRole(1, Users[0], Roles[1]));
UserRoles.Add(new UserRole(1, Users[0], Roles[2]));
UserRoles.Add(new UserRole(1, Users[1], Roles[0]));
UserRoles.Add(new UserRole(1, Users[1], Roles[1]));
UserRoles.Add(new UserRole(1, Users[2], Roles[0]));
//User A has roles: User, Admin, Superuser
//User B has roles: User, Admin
//User C has roles: User
//User D has no roles
}
public void Dispose()
{
//Do nothing
}
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public IList<UserRole> UserRoles { get; set; }
public User()
{
UserRoles = new List<UserRole>();
}
}
public class Role
{
public int RoleID { get; set; }
public string RoleName { get; set; }
public IList<UserRole> UserRoles { get; set; }
public Role()
{
UserRoles = new List<UserRole>();
}
}
public class UserRole
{
public int UserRoleID { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
public UserRole(int id, User user, Role role)
{
UserRoleID = id;
UserId = user.UserID;
User = user;
user.UserRoles.Add(this);
RoleId = role.RoleID;
Role = role;
role.UserRoles.Add(this);
}
}
Here is the modified "IsUserInRole" API that you already created :
public bool IsUserInRole(string username, string roleName)
{
var repo = new UserRepository();
return repo.Users.Any(u => u.UserName == username && u.UserRoles.Any(userrole => userrole.Role.RoleName == roleName));
}
The problem with your API was the FirstOrDefault, which restricts the navigation properties.
Related
I want users to be able to select multiple skills from a MultiSelectList dropdown. I am able to save the multiple skill selections of each user to the database, but I discovered if I remove a skill option for one user, and I log in with a different user who previously had the same skill saved in his selections, it would have been removed for this user and every other user that had similar skill option saved.
Just so I am being clear. Let's say user A had these skills saved ["C#", "Python", "Java"]. User B current currently saved skills are ["C++","Scala"]. User B then logs and decides to add the C# he has just learnt. Once he updates his profile and his selections become this ["C++","Scala", "C#"]. C# would have been removed from User A's selections so it becomes ["Python", "Java"].
This is my custom IdentityUser class.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Skills = new List<Skill>();
}
public string Location { get; set; }
public virtual ICollection<Skill> Skills { get; set; }
}
This is the Skill model.
public class Skill
{
public int SkillId { get; set; }
public string SkillType { get; set; }
}
And this is how I save the skill selections in the controller.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (ModelState.IsValid)
{
if (user.Location != profileModel.Location) user.Location = profileModel.Location;
if (profileModel.SelectedSkillIds != null)
{
List<Skill> tempSkills = new List<Skill> { };
foreach (var skillID in profileModel.SelectedSkillIds)
{
user.Skills.Add(_context.Skills.FirstOrDefault(x => x.SkillId == skillID));
var skill = _context.Skills.Find(skillID);
if (skill != null)
{
user.Skills.Add(skill);
tempSkills.Add(skill);
}
var allSkills = _context.Skills.ToList();
var skillsToRemove = allSkills.Except(tempSkills);
foreach (var sk in skillsToRemove)
{
user.Skills.Remove(sk);
}
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
return RedirectToAction("Profile", "Account");
}
return View(profileModel);
}
}
Update - How I delete selections
if (profileModel.SelectedSkillIds != null)
{
List<UserSkill> tempSkills = new List<UserSkill> { };
foreach (var skillID in profileModel.SelectedSkillIds)
{
var skill = _context.Skills.Find(skillID);
if (skill != null)
{
var userskill = new UserSkill { AppUserId = user.Id, SkillId = skill.SkillId };
user.UserSkills.Add(userskill);
tempSkills.Add(userskill);
}
var allSkills = _context.UserSkills.ToList();
var skillsToRemove = allSkills.Except(tempSkills);
foreach (var sk in skillsToRemove)
{
user.UserSkills.Remove(sk);
}
}
You should create an class like UserSkills that have UserId and SkillId and in this case any user can have multiple skills and any skill can use for many users. see many-to-many, 1,2
You should change your model to this
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Skills = new List<Skill>();
}
public string Location { get; set; }
public virtual ICollection<UserSkills> Skills { get; set; }
}
public class Skill
{
public int SkillId { get; set; }
public string SkillType { get; set; }
public virtual ICollection<UserSkills> Skills { get; set; }
}
public class UserSkills
{
public int Id { get; set }
public int UserId { get; set }
public int SkillId { get; set }
public Skill Skill { get; set; }
public ApplicationUser User { get; set; }
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (ModelState.IsValid)
{
if (user.Location != profileModel.Location) user.Location = profileModel.Location;
if (profileModel.SelectedSkillIds != null)
{
List<Skill> tempSkills = new List<Skill> { };
foreach (var sk in user.UserSkills)
{
user.UserSkills.Remove(sk);
}
foreach (var skillID in profileModel.SelectedSkillIds)
{
var userSkills = new UserSkill { UserId = user.Id, SkillId = skillID };
user.UserSkills.Add(userSkills);
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
return RedirectToAction("Profile", "Account");
}
return View(profileModel);
}
}
Then add UserSkills to your DbContext
public DbSet<Skill> Skills { get; set; }
public DbSet<UserSkill> UserSkills { get; set; }
Finally use Add-Migration and Update-DataBase in package manager console
Another options
You can inject DbContext in your controller and add UserSkills data in UserSkill Tables
private readonly YourDbContext _dbContext;
public UserController(YourDbContext dbContext)
{
_dbContext = dbContext;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (ModelState.IsValid)
{
if (user.Location != profileModel.Location) user.Location = profileModel.Location;
if (profileModel.SelectedSkillIds != null)
{
var userSkillsForDelete = _dbContext.UserSkills.Where(a => a.UserId == user.Id).ToList();//<-- NOTE THIS
foreach (var sk in userSkillsForDelete)
{
//user.UserSkills.Remove(sk);
_dbContext.UserSkills.Remove(sk);<--NOTE THIS
}
foreach (var skillID in profileModel.SelectedSkillIds)
{
var userSkills = new UserSkill { UserId = user.Id, SkillId = skillID };
_dbContext.UserSkills.Add(userSkills);<--NOTE THIS
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
return RedirectToAction("Profile", "Account");
}
return View(profileModel);
}
}
I have a small api with these models with ef-core
public class AppUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public class Account
{
public int id { get; set; }
public AppUser User { get; set; }
public string AccountNumber { get; set; }
}
and I have a controller get function:
[HttpGet]
public async Task<IActionResult> Home()
{
// retrieve the user info
//HttpContext.User
var userId = _caller.Claims.Single(c => c.Type == "id");
var user = await _appDbContext.Users.SingleAsync(c => c.Id == userId.Value);
return new OkObjectResult(new
{
Message = "This is secure API and user data!",
user.FirstName,
user.LastName,
user.Accounts
});
}
How do I get the controller to return the list of accounts? In debug I can see the "accounts" variable with the correct data, I'm just not sure on how to format that var and return it in the api
I am currently recievng:
{
"message": "This is secure API and user data!",
"firstName": "Bill",
"lastName": "Johnson",
"serviceAccounts": null
}
UPDATE: So I ran the app using the dotnet command line and got
{"firstName":"Bill","lastName":"Johnson","serviceAccounts":[{"id":1,"camsUser":{"firstName":"Bill","lastName":"Johnson","company":null
Iif the child entities are setup for lazy loading then you should fetch the accounts and user as
var user = await _appDbContext.Users.Include(u => u.Accounts).SingleAsync(c => c.Id == userId.Value);
I figured it out with this:
var userId = _caller.Claims.Single(c => c.Type == "id");
var user = await _appDbContext.Users.Include(s =>s.Accounts).SingleAsync(c => c.Id == userId.Value);
var accounts = user.Accounts.Select(a => new
{
a.AccountNumber,
a.id
});
return new OkObjectResult(new
{
user.FirstName,
user.LastName,
accounts
});
I have added the following UserLog table in the database using asnetidentity but i don't know how to perform add,update operation on the table.
public class User : IdentityUser
{
public virtual ICollection<UserLog> UserLogs { get; set; }
}
public class Context : IdentityDbContext<User, Role, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public Context(string connString)
: base(connString)
{
}
}
public class UserLog
{
[Key]
public Guid UserLogID { get; set; }
public string IPAD { get; set; }
public DateTime LoginDate { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser User { get; set; }
}
public System.Data.Entity.DbSet<UserLog> UserLog { get; set; }
Like in the following code, I am using inbuilt methods of these aspnetidentity tables but how to add an entry in the "UserLog" table when user login every time in the following method?
public override async Task AuthenticateLocalAsync(LocalAuthenticationContext ctx)
{
var username = ctx.UserName;
var password = ctx.Password;
var message = ctx.SignInMessage;
ctx.AuthenticateResult = null;
if (userManager.SupportsUserPassword)
{
var user = await FindUserAsync(username);
if (user != null)
{
if (userManager.SupportsUserLockout &&
await userManager.IsLockedOutAsync(user.Id))
{
return;
}
if (await userManager.CheckPasswordAsync(user, password))
{
if (userManager.SupportsUserLockout)
{
await userManager.ResetAccessFailedCountAsync(user.Id);
}
var result = await PostAuthenticateLocalAsync(user, message);
if (result == null)
{
var claims = await GetClaimsForAuthenticateResult(user);
result = new AuthenticateResult(user.Id.ToString(), await GetDisplayNameForAccountAsync(user.Id), claims);
}
ctx.AuthenticateResult = result;
}
else if (userManager.SupportsUserLockout)
{
await userManager.AccessFailedAsync(user.Id);
}
}
}
}
Step 1: Create an instance of UserLog
var userLog = new UserLog() { /* Set value for all the properties here */ };
Step 2: Add instance of UserLog to DbContext
context.Set<UserLog>().Add(userLog);
Step 3: Call DbContext.SaveChanges() to save changes to database.
context.SaveChanges();
Complete source code will looks like:
var userLog = new UserLog {
UserLogID = Guid.NewGuid(),
IPAD = "Some Value",
LoginDate = DateTime.Now,
UserId = "User Id Here"
};
var context = new Context();
context.Set<UserLogs>().Add(userLog);
context.SaveChanges();
One way to fix this problem is to turn lazy loading off ..... if i do not want to turn off lazy loading, is there other solution?
my action is look like this
[ResponseType(typeof(AspNetUsers))]
public IHttpActionResult Get(string id)
{
var user = db.AspNetUsers.FirstOrDefault(s => s.UserName == id);
if (user == null)
return null; //":کاربر نامعتبر";
JsonResult js = new JsonResult();
js.Data = user;
js.ContentEncoding = Encoding.UTF8;
js.ContentType = "Application/json";
js.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
//var returnUser = user.UserName + user.Email + user.FirstName + user.LastName +
// user.AspNetRoles.Select(s => s.Name);
return Ok(js);/// or return ok(user)
}
You are mixing MVC and Web Api concepts and object.
JsonResult is an MVC derived ActionResult object while IHttpActionResult is associated with Web API.
Also, if you don't want to return all the data then don't. Construct a new object result model and return only what is needed.
[ResponseType(typeof(UserModel))]
public IHttpActionResult Get(string id) {
var user = db.AspNetUsers.FirstOrDefault(s => s.UserName == id);
if (user == null)
return NotFound();
var data = new UserModel {
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Roles = user.AspNetRoles.Select(s => s.Name).ToArray()
//...other properties you want to return
};
return Ok(data);
}
public class UserModel {
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] Roles { get; set; }
}
i fix my Problem whit little change in Nkosi answer .....
[ResponseType(typeof(UserModel))]
public IHttpActionResult Get(string id)
{
var user = db.AspNetUsers.Include(a=>a.AspNetRoles).Where(s => s.UserName == id).FirstOrDefault();
if (user == null)
return NotFound();
var data = new UserModel
{
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Roles = user.AspNetRoles.Select(s => s.Name).ToArray()
//...other properties you want to return
};
return Ok(data);
}
public class UserModel
{
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] Roles { get; set; }
}
thanks alot
I'm writing an mvc 4 c# .net 4.5 website
I want to create a new company object and register a new user that is linked to that company.
My account model is:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string PhoneNumber { get; set; }
public bool MarketingEmailOptin { get; set; }
public bool isDisabled { get; set; }
public virtual Company CompanyICanEdit { get; set; }
}
If i call the following it adds the user fine but has null for the CompanyICanEdit field:
WebSecurity.CreateUserAndAccount(addCompanyViewModel.User.UserName,
addCompanyViewModel.User.Password,
propertyValues: new
{
FirstName = addCompanyViewModel.User.FirstName,
LastName = addCompanyViewModel.User.LastName,
EmailAddress = addCompanyViewModel.User.EmailAddress,
PhoneNumber = addCompanyViewModel.User.PhoneNumber,
MarketingEmailOptin = addCompanyViewModel.User.MarketingEmailOptin,
isDisabled = false
});
which i would expect as i am not assigning it anything.
i have tried adding (mycompany is a company object):
WebSecurity.CreateUserAndAccount(addCompanyViewModel.User.UserName,
addCompanyViewModel.User.Password,
propertyValues: new
{
FirstName = addCompanyViewModel.User.FirstName,
LastName = addCompanyViewModel.User.LastName,
EmailAddress = addCompanyViewModel.User.EmailAddress,
PhoneNumber = addCompanyViewModel.User.PhoneNumber,
MarketingEmailOptin = addCompanyViewModel.User.MarketingEmailOptin,
isDisabled = false,
CompanyICanEdit = mycompany
});
But i get an error saying it can't match the type.
How do i go about registering the user so that the CompanyICanEdit contains the CompanyId value of mycompany?
Any help will be appreciated. thanks
Never worked out how to do it in 1 go, got round it by the following in the end if anyone has the same problem.
//
// POST: /BusinessManager/ManageCompanies/Add
[HttpPost]
public ActionResult Add(AddCompanyViewModel addCompanyViewModel)
{
if (ModelState.IsValid)
{
// Create company and attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(addCompanyViewModel.User.UserName,
addCompanyViewModel.User.Password,
propertyValues: new
{
FirstName = addCompanyViewModel.User.FirstName,
LastName = addCompanyViewModel.User.LastName,
EmailAddress = addCompanyViewModel.User.EmailAddress,
PhoneNumber = addCompanyViewModel.User.PhoneNumber,
MarketingEmailOptin = addCompanyViewModel.User.MarketingEmailOptin,
isDisabled = false
});
db.Companies.Add(addCompanyViewModel.Company);
var newuser = db.UserProfiles.FirstOrDefault(u => u.UserName == addCompanyViewModel.User.UserName);
if (newuser != null)
{
newuser.CompanyICanEdit = addCompanyViewModel.Company;
db.Entry(newuser).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError("", "New user wasn't added");
}
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", Mywebsite.Controllers.AccountController.ErrorCodeToString(e.StatusCode));
}
}
return View(addCompanyViewModel);
}