Adding custom properties from AspNetUsers Table to /Manage/Index in .net mvc - c#

I'm using .net mvc authentication and i've added a few different fields to the AspNetUsers table like First Name. When the user registers into the system including entering their first name it is inputted into that table and that works fine.
What I want to do is make a few of the fields (eg. firstname, lastname) from AspNetUsers editable in /Manage/Index. /Manage/Index uses the IndexViewModel which is below.
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
The controller action is:
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage = message == ManageMessageId.ChangePasswordSuccess ?
var userId = User.Identity.GetUserId();
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
};
return View(model);
}
My questions are:
Should I just create my own ChangeDetails view and controller and change firstname, lastname, etc in there?
Manage/Index uses #model IndexViewModel - i'm not entirely sure how to access the AspNetUser table with that IndexViewModel - i suppose if userId is available i could just query the table with that id.
Can all this be done easily in Manage/Index and i'm making it harder than necessary.
Thanks.

If you've added the additional properties to the ApplicationUser class in Models\IdentityModels.cs, You can modify IndexViewModel.cs like so:
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
public ApplicationUser CurrentUser { get; set; }
}
Then, in the Index action method of ManageController.cs, get the ApplicationUser reference for the current user using the UserManager:
var userId = User.Identity.GetUserId();
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId),
CurrentUser = await UserManager.FindByIdAsync(userId)
};
return View(model);

Related

Associated Model class not being saved keeps returning null

I have a User model inside my Ticket model but every time I try and set the User inside the Ticket model, it always just ends up being null when I try and retrieve it later on and I cannot seem to figure out why.
The goal is to be able to create a Ticket, when creating you can type in a username to associate this ticket to someone. If the user is found, the ticket is created.
However, it does not seem to save the associated User that was typed in or any information regarding it.
Models
public class Ticket
{
[Key]
public Guid Id { get; set; }
[Required]
public string Title { get; set; }
public User owningUser { get; set; }
}
public class User
{
public Guid UserId { get; set; }
[Required]
public string Username { get; set; }
public List<Ticket> assignedTickets { get; set; }
}
Create.razor
<InputText id="title" #bind-Value="#ticketItem.owningUser.Username" class="form-control" aria-label="Disabled input example" />
private Ticket ticketItem { get; set; } = new Ticket();
protected override async Task OnInitializedAsync()
{
ticketItem.owningUser = new User();
}
public async Task CreateNewTodoItem()
{
var result = await Http.PostAsJsonAsync("TodoItems", ticketItem);
}
Controller
public async Task <ActionResult> CreateNewTodoItem(Ticket item)
{
ApplicationUser userValid;
User tempUser = new User();
try
{
userValid = userManager.Users.Where(s => s.Email == item.owningUser.Username).First();
item.owningUser = new User();
item.owningUser.Username = userValid.Email;
context.Tickets.Add(item);
await context.SaveChangesAsync();
return NoContent();
} catch (InvalidOperationException)
{
return BadRequest();
}
}
Retrieval method
private User associatedUser { get; set; }
[Parameter]
public Guid Id { get; set; }
protected override async Task OnInitializedAsync()
{
ticketItem = await Http.GetFromJsonAsync<Ticket>($"TodoItems/{Id}");
associatedUser = await Http.GetFromJsonAsync<User>($"User/{Id}");
ticketItem.owningUser = associatedUser;
}
// Controller ---
[HttpGet("{ticketID}")]
public async Task<ActionResult<User>> GetUserForTicket(Guid ticketID)
{
var ticketUser = context.Tickets.First(p => p.Id == ticketID);
User use = new User { Username = ticketUser.owningUser.Username };
return Ok(use);
}
Null error
DBContext
public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
{
public ApplicationDbContext(
DbContextOptions options,
IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
{
}
public DbSet<Company> Companies { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
}
you have to include owningUser
var ticketUser = context.Tickets.Include(o=> o.owningUser).First(p => p.Id == ticketID);
if(ticketUser.owningUser!=null)
return new User { Username = ticketUser.owningUser.Username };
return BadRequest("no owningUser");
But it can be that owningUser is null or your dbcontext is not configured properly since I can see that your Create action doesn't make any sense at all.

Identity Roles with Database First ASP.Net MVC 5 and Entity Framework

I am trying to follow tutorials online to help me get roles setup in my MVC project, but seem to be failing to understand everything. My company required that I create my project Database First, rather than code first, so my roles already exist in my dbContext. I do not want to create a controller to Index, Create, Edit, etc. my Roles, at least not at this step, but I DO want to know what userGroup my users belong to, to then refer to their userGroup as their role.
So, I have two Models; one for users and one for userGroups (essentially a "roles" table):
public partial class User : IdentityUser
{
public int UKey { get; set; }
public int UgKey { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string EmailAddress { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool UseBrightreeAuth { get; set; }
public bool Active { get; set; }
}
public partial class UserGroup
{
public int UgKey { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
I have a Account Controller that handles the "logging in" of my website:
public ActionResult Login(LoginViewModel login)
{
if (!ModelState.IsValid)
{
ViewBag.Error = "Form is not valid; please review and try again.";
return View("Login");
}
using (var wfc = new WorkflowContext())
{
if(wfc.User.Where(x => x.Username == login.Username).Any())
{
//ignore the bad logic/LINQ, it's temporary...
var UserManager = new UserManager<User>(new UserStore<User>(wfc));
UserManager.AddToRole("UserName", "UserRole");
}
else
{
ViewBag.Error = "Credentials invalid. Please try again.";
return View("Login");
}
}
}
the following line of code will not compile, saying wfc is not a valid argument, but I am not sure what I should be using instead?
//Where wfc is the Context that contains my User and UserGroup Tables
var UserManager = new UserManager<User>(new UserStore<User>(wfc));
I get the error
Cannot convert from "Workflow.Models.WorkflowContext" to "System.Data.Entity.DbContext"
Is there a better way to add a user to a role or am I misunderstanding the process? Thanks.

Adding custom field to identitymodel

I have added a new field to MVC's identity model,
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AccountID { get; set; }
public int JeAdmin { get; set; }
public Ekipa Ekipa { get; set; }
}
My ekipa class consists of the following:
public class Ekipa
{
public int id { get; set; }
public string Ime { get; set; }
public int LeaderID { get; set; }
public int Geslo { get; set; }
}
How do I set an object, which I get from database, to the logged in user?
I get the current user,
ApplicationDbContext db = new ApplicationDbContext();
var currentUserId = User.Identity.GetUserId();
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var currentUser = manager.FindById(User.Identity.GetUserId());
EkipaDb db1 = new EkipaDb();
currentUser.Ekipa = db1.Ekipa.Where(m => m.id == 1);
How do I get the object from EkipaDb where id is 1, and add it to the current user?
You can't add an object to a database table's column - but you can add a foreign key.
Add:
public int EkipaId { get; set; }
And make the Ekipa object virtual:
[ForeignKey("EkipaId")]
public virtual Ekipa Ekipa { get; set; }
This will store the Id of the Epika object in the ApplicationUser table, and you can use the navigation property to retrieve the object itself when loading the ApplicationUser.

Add object as property in ASP.Net identity

I'm using MVC 5 ASP.net Identity entity framework code-first to create an online application form. I have created a new project with the ASP.net identity scaffold, and need to be able to add additional properties. Some of them are simple properties- mobile phone number etc, and this works fine. however I need to add a more complex object to the user to store the application form information. I tried doing this:
public class ApplicationUser : IdentityUser
{
public string Title { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public override string PhoneNumber { get; set; }
public string PracticeName { get; set; }
public string Address { get; set; }
public string Mobile { get; set; }
public string GMCNumber { get; set; }
public AppForm ApplicationForm { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<AppForm> AppForms { get; set; }
public DbSet<AppFormDocument> AppFormDocuments { get; set; }
public DbSet<AppFormAnswer> AppFormAnswers { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
and have created the appform models like this:
public class AppForm {
public int Id { get; set; }
public int PercentComplete { get; set; }
public string Status {get; set; }
public bool Completed { get; set; }
public bool Reviewed { get; set; }
public bool SignedOff { get; set; }
public int LastPageCompleted { get; set; }
public List<AppFormDocument> Documents { get; set; }
public List<AppFormAnswer> Answers { get; set; }
}
public class AppFormDocument {
public int Id { get; set; }
public DateTime DateSubmitted { get; set; }
public string Name { get; set; }
public DateTime? ExpiryDate { get; set; }
public bool Accepted { get; set; }
public string ScannedFile { get; set; }
}
public class AppFormAnswer {
public int Id { get; set; }
public string QuestionNumber { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
The application form is very large and has many questions which is why I didnt just put it all in the applicationuser class. There is also the requirement to upload documents with the form.
Now, when I create the user and attach an instance of the AppForm and some instances of AppFormAnswers to it, then save it, the data gets stored successfully, but when I try to access the data again after logging in, it is not found. The additional simple properties of the user are available though, including mobile number and title. here's my code:
[Route("appform/{action}")]
[Authorize]
public class AppFormController:Controller {
// GET: AppForm
public ActionResult Index() {
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
var model = new AppFormIndexViewModel();
if (user != null) {
if (user.ApplicationForm != null) {
model.PercentComplete = user.ApplicationForm.PercentComplete;
model.NextPage = "Page" + user.ApplicationForm.LastPageCompleted + 1;
model.Status = user.ApplicationForm.Status;
} else {
// if appform is not available for user for some reason, create a new one.
user.ApplicationForm = new AppForm { PercentComplete = 0, Reviewed = false, Status = "Incomplete", SignedOff = false, Completed = false, LastPageCompleted = 0 };
var uManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
uManager.UpdateAsync(user);
model.PercentComplete = 0;
model.Status = "Incomplete";
model.NextPage = "Page1";
}
}
return View(model);
}
Now when the uManager.UpdateAsync(user) line runs, the data is saved to the database fine and a new appForm record is added. The database automatically creates primary keys and foreign keys for all the tables too.
So, do I need to write an overload of the login method to retrieve the data from the other tables? Or do I need to add something to the ApplicationDbContext class?
This is my first MVC application so not really sure where to turn now. Have tried reading many forum posts and blogs but not found anything that really matches my needs. A lot of information relates to earlier version of mvc which do not work with the newer asp.net identity system.
I finally managed to figure it out. I added an appformid property to the applicationuser class like this:
[ForeignKey("AppForm")]
public int AppFormId { get; set; }
Then created another class to load the appform object like this:
public static class AppFormManager {
public static ApplicationUser GetCurrentUser() {
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
if (user != null) {
AppForm form = new AppForm();
ApplicationDbContext db = new ApplicationDbContext();
AppForm ap = db.AppForms.Where(af => af.Id == user.AppFormId).Include("Answers").Include("Documents").SingleOrDefault();
if (ap == null) {
var AppForm = new AppForm { PercentComplete = 0, Reviewed = false, Status = "Incomplete", SignedOff = false, Completed = false, LastPageCompleted = 0 };
user.AppForm = AppForm;
} else {
user.AppForm = ap;
}
return user;
}
return null;
}
public static bool SaveCurrentUser() {
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
if (user == null) { return false; }
var uManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
uManager.UpdateAsync(user);
return true;
}
}
So in the controller, the code is much cleaner:
// GET: AppForm
public ActionResult Index() {
ApplicationUser user = AppFormManager.GetCurrentUser();
var model = new AppFormIndexViewModel();
if (user != null) {
model.PercentComplete = user.AppForm.PercentComplete;
model.NextPage = "Page" + user.AppForm.LastPageCompleted + 1;
model.Status = user.AppForm.Status;
}
return View(model);
}
and I can call the AppFormManager.SaveCurrentUser() method to save the data in the post action.
Thanks for all those who made suggestions which helped me figure out a way to do it. Possibly not the best way to do it, but it works for me for now.

ASP.NET MVC insert in two separate tables

Hello I am a newbie in ASP.NET MVC.
I have three classes Login, User and TheTradersContext as you can see below:
namespace SimpleUser.Models
{
[Table("login")]
public class Login
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Email")]
public string email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string password { get; set; }
public string status { get; set; }
public string salt { get; set; }
}
}
namespace SimpleUser.Models
{
[Table("userdata")]
public class User
{
public int ID { get; set; }
public string surname { get; set; }
public string name { get; set; }
public string sex { get; set; }
public string city { get; set; }
public string address { get; set; }
public string zipcode { get; set; }
public string tel { get; set; }
public string bdate { get; set; }
public string country { get; set; }
}
}
namespace SimpleUser.Models
{
public class TheTradersContext : DbContext
{
public DbSet<Login> loginusers { get; set; }
public DbSet<User> allusers { get; set; }
}
}
Then i created a LoginController.cs that has a register function in which i try to pass in two different tables of my database the elements that i take from formcollection as you can see below. The problem is that in my database are passing only in the table login and not in the userdata.
[HttpPost]
public ActionResult Registration(FormCollection forms){
var db = new TheTradersContext();
var crypto = new SimpleCrypto.PBKDF2();
var p = forms["password"].ToString();
String encryptPass = crypto.Compute(p);
var newUser = db.loginusers.Create();
var nuser = db.allusers.Create();
newUser.email = forms["email"].ToString();
newUser.password = encryptPass;
newUser.status = "user";
newUser.salt = crypto.Salt;
//nuser.ID=Convert.ToInt32("18");
nuser.surname = forms["lastname"].ToString();
nuser.name = forms["firstname"].ToString();
nuser.sex = forms["gender"].ToString();
nuser.city = forms["city"].ToString();
nuser.address = forms["addr"].ToString();
nuser.zipcode =forms["zip"].ToString();
nuser.tel = "fdgfdgf".ToString();
nuser.country = forms["country"].ToString();
nuser.bdate ="dsafdsaf".ToString();
try
{
db.loginusers.Add(newUser);
db.SaveChanges();
var useri = db.loginusers.Single(u => u.email == newUser.email);
if (useri == null) {
throw new Exception();
}
nuser.ID = Convert.ToInt32(useri.ID);
db.allusers.Add(nuser);
db.SaveChanges();
}
catch (Exception x)
{
ModelState.AddModelError("", "This username is in use");
}
return View();
}
My table in database has exactly the same names of the fields on user.
Of course I tried to exclude the code that has to do with the login and pass only the values of the userdata in database but i saw the exception : System.Data.Entity.Infrastructure.DbUpdateException.
I have tried a lot of things until now... any idea?
from your code it seems that there is a relation 1 to many between Login and User, each login has 1 or more user ( i figured this out since you were trying to put nuser.ID = Convert.ToInt32(useri.ID);)
in your class User put a navigational property called public Login Login{get; set;}
and the User class should has a primary key let us say ( UserId) unless you marked ID as primary key then your User table is a weak entity.
and mark the ForeignKey attribute for the new property Login as ForeignKey("ID")
after doing this, then easily you can do the following
var login=new Login();
// fill out the login data
db.loginusers.Add(login)
db.allusers.Add(new User(){
surname = forms["lastname"].ToString(),
name = forms["firstname"].ToString(),
sex = forms["gender"].ToString(),
city = forms["city"].ToString(),
address = forms["addr"].ToString(),
zipcode =forms["zip"].ToString(),
tel = "fdgfdgf".ToString(),
country = forms["country"].ToString(),
bdate ="dsafdsaf".ToString(),
Login=login
});
db.SaveChanges();
hope that his will help you
note: your classes design can be improved and normalized to reflect the database relationship

Categories