Adding custom field to identitymodel - c#

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.

Related

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

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);

Combining AppDbContext And IdentityDbContext Issue

I am Using EF 6.0 and want to combine AppDbContext & IdentityDbContext into a single context which is AppDbContext.
This is a requirement as i have other tables which has relations with the AspNetUsers table created by EF.
The problem is EF is creating two tables for Users such as AspNetUsers and IdentityUsers.
Also If i use DbSet<ApplicationUser> in DbContext instead of DbSet<IdentityUsers> , Then add-migration throws up error.
My AppDbContext is
public class AppDbContext : IdentityDbContext<ApplicationUser>,IDisposable
{
public AppDbContext()
: base("MvcArchBSEFDB", throwIfV1Schema: false)
{
}
public static AppDbContext Create()
{
return new AppDbContext();
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
//public DbSet<ApplicationUser> AppUsers { get; set; } // breaks migration
//Multiple object sets per type are not supported. The object sets 'AppUsers ' and 'Users' can both contain instances of type 'MvcArchBS.DAL.Setp.ApplicationUser'.
public DbSet<IdentityUser> AppUsers { get; set; } // Creates Two Tables AspNetUsers & IdentityUser
public DbSet<IdentityUserRole> UserRoles { get; set; }
public DbSet<IdentityUserClaim> Claims { get; set; }
public DbSet<IdentityUserLogin> Logins { get; set; }
public DbSet<Module> Modules { get; set; }
public DbSet<SubModule> SubModules { get; set; }
public DbSet<PageMst> Pages { get; set; }
public void Dispose()
{
base.Dispose();
}
}
And My ApplicationUser Class is
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
status = "A";
reptngusrid = "admin";
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int usrid { get; set; }
public string usrdescr { get; set; }
public int empid { get; set; }
public int usrgrpid { get; set; }
[StringLength(1)]
public string status { get; set; }
public string reptngusrid { get; set; }
public int defmodid { get; set; }
[StringLength(20)]
public string cltur { get; set; }
[ForeignKey("defmodid")]
public virtual Module Module { 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;
}
}
How do i get this to work ? I want to use something like context.AppUsers in my queries which i am unable to get.
It turns out my Service Layer Project also needs a reference to Microsoft.AspNet.Identity.EntityFramework.dll.
Once i added that all identity entity sets are available now with my context.
Also as tmg & Brendan had pointed out the IdentityDbContext members DbSets are not required in the AppDbContext.
Hope this helps someone.

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.

Creating inheritance users from base asp.net identity user

I have problem in which i would like to create N, two in the example, user objects (e.g. Customer & Supplier) which all inherent from the asp.net IdentityUser object. These object have very different additional data besides the the data from the IdentityUser. I would like to use the IdentityUser user as this gives me a flexible way of taking care of authentication and authorization.
This example has been very stripped down but should supply sufficient information concerning the not being able to create a concrete user (e.g. Customer of Supplier). It seems i need to use the UserManager object as this also takes care of creating for example the password hash and additional security information.
I get presented the following error:
{"Attaching an entity of type 'Supplier' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate."}
Classes which inherent from IdentityUser
public class Customer : IdentityUser
{
public string CustomerProperty { get; set; }
}
public class Supplier : IdentityUser
{
public string SupplierProperty { get; set; }
}
Database context class
public class ApplicationDbContext : IdentityDbContext {
public ApplicationDbContext() : base("ApplicationDbContext")
{
Database.SetInitializer(new ApplicationDbInitializer());
}
public DbSet<Customer> CustomerCollection { get; set; }
public DbSet<Supplier> SupplierCollection { get; set; }
}
Seeding class which throws the exception
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
var userStore = new UserStore(context);
var userManager = new UserManager(userStore);
// Seed customer user which inherents from asp.net IdentityUser
var user = userManager.FindByEmail("customer#customer.com");
if (user == null)
{
user = new User()
{
UserName = "customer#customer.com",
Email = "customer#customer.com"
};
userManager.Create(user, userPassword);
var customerUser = new Customer()
{
Id = user.Id,
CustomerProperty = "Additional Info"
};
context.Entry(customerUser).State = EntityState.Modified;
context.SaveChanges();
}
// Seed supplier user which inherents from asp.net IdentityUser
var user = userManager.FindByEmail("supplier#supplier.com");
if (user == null)
{
user = new User()
{
UserName = "supplier#supplier.com",
Email = "supplier#supplier.com"
};
userManager.Create(user, userPassword);
var supplierUser = new Supplier()
{
Id = user.Id,
IBAN = "212323424342234",
Relationship = "OK"
};
context.Entry(supplierUser).State = EntityState.Modified;
context.SaveChanges();
}
}
}
**** UPDATE ****
The solution below works but i am still struggling with two issues:
I would always like to have one user type (e.g. Customer of Supplier) associated with the IdentityUser. I though about using an interface but this doesn't work.
If i also add the virtual reference towards the IdentityUser on the user types i get an 'Unable to determine the principal end of an association between the types 'ApplicaitonUser' and 'Supplier'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.' exception.
Classes
public class Customer
{
[Key]
public int CustomerId { get;set; }
public string CustomerProperty { get; set; }
*public virtual User User { get; set; }*
}
public class Supplier
{
[Key]
public int SupplierId { get;set; }
public string SupplierProperty { get; set; }
*public virtual User User { get; set; }*
}
**Class IdentityUser (which works) **
public class User : IdentityUser
{
public virtual Supplier Supplier { get; set; }
public virtual Customer Customer { get; set; }
}
**Class IdentityUser (what i would like) **
public class User : IdentityUser
{
public virtual IConcreteUser ConcreteUser{ get; set; }
}
Database context class
public class ApplicationDbContext : IdentityDbContext {
public ApplicationDbContext() : base("ApplicationDbContext")
{
Database.SetInitializer(new ApplicationDbInitializer());
}
public DbSet<Customer> CustomerCollection { get; set; }
public DbSet<Supplier> SupplierCollection { get; set; }
}
**Seeding class **
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
var userStore = new UserStore(context);
var userManager = new UserManager(userStore);
var roleManager = new RoleManager(roleStore);
var user = userManager.FindByEmail("customer#customer.com");
if (user == null)
{
user = new ApplicationUser()
{
UserName = "customer#customer.com",
Email = "customer#customer.com"
Customer = new Customer()
{
CustomerProperty = "Additional Info"
}
};
userManager.Create(user, userPassword);
roleManager.AddUserToRole("Customer");
}
user = userManager.FindByEmail("supplier#supplier.com");
if (user == null)
{
user = new ApplicationUser()
{
UserName = "supplier#supplier.com",
Email = "supplier#supplier.com",
Supplier = new Supplier()
{
IBAN = "212323424342234",
Relationship = "OK"
}
};
userManager.Create(user, userPassword);
roleManager.AddUserToRole("Supplier");
}
}
}
As others do too I think this is a design problem. There are some alternative approaches like:
use roles to define the "user-type" (a user can be supplier AND customer)
make the Supplier and Customer entities a relation not extension of the user
e.g.:
public class ApplicationUser : IdentityUser
{
public virtual Customer Customer { get; set; }
public virtual Supplier Supplier { get; set; }
}
public class Customer
{
[Key]
public int Id { get; set; }
public virtual ApplicationUser User { get; set; }
public string CustomerProperty { get; set; }
}
public class Supplier
{
[Key]
public int Id { get; set; }
public virtual ApplicationUser User { get; set; }
public string SupplierProperty { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
}
public class ApplicationDbInitializer
: DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
var userStore = new UserStore(context);
var userManager = new UserManager(userStore);
var roleManager = new RoleManager(roleStore);
var user = userManager.FindByEmail("customer#customer.com");
if (user == null)
{
user = new ApplicationUser()
{
UserName = "customer#customer.com",
Email = "customer#customer.com"
Customer = new Customer()
{
CustomerProperty = "Additional Info"
}
};
userManager.Create(user, userPassword);
roleManager.AddUserToRole("Customer");
}
user = userManager.FindByEmail("supplier#supplier.com");
if (user == null)
{
user = new ApplicationUser()
{
UserName = "supplier#supplier.com",
Email = "supplier#supplier.com",
Supplier = new Supplier()
{
IBAN = "212323424342234",
Relationship = "OK"
}
};
userManager.Create(user, userPassword);
roleManager.AddUserToRole("Supplier");
}
}
}
and in your logic you can do something like:
if (User.IsInRole("Customer"))
{
// do something
}
DISCLAIMER: This is not a "copy&paste" example and should just give you an idea of a different approach.
I just resolved a similar problem. I created a navigation property of abstract type DomainUser in my AppUser (that inherits from Identity User)
public class AppUser : IdentityUser
{
public DomainUser DomainUser { get; set; }
}
DomainUser looks like this:
public abstract class DomainUser : IAggregateRoot
{
public Guid Id { get; set; }
public AppUser IdentityUser { get; set; }
}
I inherit from DomainUser in all concrete domain user types:
public class AdministrationUser : DomainUser
{
public string SomeAdministrationProperty { get; set; }
}
public class SupplierUser : DomainUser
{
public string SomeSupplierProperty { get; set; }
}
public class Customer : DomainUser
{
public string SomeCustomerProperty { get; set; }
}
And in DbContext in OnModelCreating method I configured Entity Framework to store all entities inherited from DomainUser in separate tables (it's called Table per Concrete Type). And configured one to one relationship between IdentityUser and DomainUser:
modelBuilder.Entity<DomainUser>()
.Map<AdministrationUser>(m =>
{
m.MapInheritedProperties();
m.ToTable("AdministrationUsers");
})
.Map<SupplierUser>(m =>
{
m.MapInheritedProperties();
m.ToTable("SupplierUsers");
})
.Map<Customer>(m =>
{
m.MapInheritedProperties();
m.ToTable("Customers");
});
modelBuilder.Entity<DomainUser>()
.HasRequired(domainUser => domainUser.IdentityUser)
.WithRequiredPrincipal(groomUser => groomUser.DomainUser);
This code added column "DomainUser_Id" to table AspNetUsers and now I'm able to access IdentityUser navigation property in each domain user and DomainUser navigation property in AppUser.

How can I get result of specified table according to user role in collection using Identity 2.0 .NET MVC

I have one table that represents for example Fruit which has a collection of ApplicationUser. Then in ApplicationUser I add Fruit and FruitId.
So what I need is the result of Fruit which is according to user role of ONE of ApplicationUser in Fruit.
Here is the model.
public class Fruit
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<ApplicationUser> User { get; set; }
}
public class ApplicationUser : IdentityUser
{
// some code
public int FruitId { get; set; }
public virtual Fruit Fruit { get; set; }
}
And what I tried is here, but with no success.
var roleManager = new RoleManager<IdentityRole>
(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var role = roleManager.FindByName("Simple");
var SimpleFruits = db.Fruits.Where(f => f.User.FirstOrDefault(u => u.Roles.Select(y => y.RoleId)).Contains(role.Id));

Categories