Reputation: 1339
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.
Upvotes: 1
Views: 2385
Reputation: 1339
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.
Upvotes: 1