MVC one-to-many database not updated - asp.net

I have added an ApplicationCompany class to my MVC project like this:
public class ApplicationCompany
{
public Guid Id { get; set; }
public String Name { get; set; }
public virtual List<ApplicationUser> Users { get; set; }
public ApplicationCompany()
{
Id = Guid.NewGuid();
Users = new List<ApplicationUser>();
}
}
In the ApplicationUser class I have added:
public virtual ApplicationCompany Company { get; set; }
Now, when I debug an instance of ApplicationUser, I can see it has the Company property, and I can see the ApplicationCompany table in my database. When a user registers, I'd like to add a Company linked to them. I am doing the following but it is not saving the company:
using (var context = new ApplicationDbContext())
{
if (ModelState.IsValid)
{
var user = new ApplicationManager
{
UserName = model.Email,
Email = model.Email
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var company = new ApplicationCompany();
user.Company = company;
context.SaveChanges();
...
}
}
}
Any ideas why?

The DbContext instance you use into the if (result.Succeeded) block doesn't know anything about the user instance you just set before calling context.SaveChanges(). Modify your code by attaching user instance into the context or just changing the state of the user to EntityState.Modified.
if (result.Succeeded)
{
var company = new ApplicationCompany();
user.Company = company;
context.Entry(user).State = EntityState.Modified;
context.Entry(company).State = EntityState.Added;
context.SaveChanges();
}
Edit 1:
Attaching an entity of type 'ApplicationUser' failed because another entity of the same type already has the same primary key value
Because another context own the user instance, then you must instantiate the DbContext into the if block (don't forget to suppress the root instance you create at the start of your method) like this:
if (result.Succeeded)
{
using (var context = new ApplicationDbContext())
{
var company = new ApplicationCompany();
user.Company = company;
context.Entry(user).State = EntityState.Modified;
context.Entry(company).State = EntityState.Added;
context.SaveChanges();
}
}
Edit 2: Overriding CreateAsync method in ApplicationUserManager class
The better approcach, IMHO, is to oveeride CreateAsync method in ApplicationUserManager class. by doing that you don't need to add the company updates into your controller's action.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
// ...
public override async Task<IdentityResult> CreateAsync(ApplicationUser user, string password)
{
var company = new ApplicationCompany();
user.Company = company;
return await base.CreateAsync(user, password);
}
}

The problem is that, you are using a different DbContext object to insert the User than the one you are using to insert the company. You should be using the same DbContext object.
Change your UserManager.CreateAsync method to accept the DbContext object and use it there to add the new user
public Task<SomeDTO> CreateAsync(ApplicationDbContext db, ApplicationUser user,
string password)
{
//to do : Set the password
//Use db to add the new User
db.ApplicationUsers.Add(user);
await db.SaveChangesAsync();
// to do : return something
}
And when you call it from your action method, make sure to pass the DbContext.
var result = await UserManager.CreateAsync(context,user, model.Password);
That should fix your problem.

Related

Asp.Net IdentityUser add custom List Property

I am fairly new to coding with asp.net so there might be an obvious answere to my question but I haven't found one yet.
So currently I am developing a site for project management and I want the users to get notified when an event happens, eg. they were added to a new project, a project has been updated etc.
For that I have expanded the IdentityUser Model with a new property List
public class CojectUser : IdentityUser
{
public List<Notification> Notifications { get; set; }
}
public class Notification
{
public int NotificationID { get; set; }
public string Message { get; set; }
public bool Seen { get; set; }
}
When an event happens I add them to the user's notification list and update the user via the userManager.
public class EventBroker<T> : IEventBroker<T>
{
private readonly UserManager<CojectUser> userManager;
public EventBroker(UserManager<CojectUser> userMgr, IUserValidator<CojectUser> userValid)
{
userManager = userMgr;
}
public async Task NotifyAsync(Message<T> message, List<UserRole> recipients)
{
foreach (var user in recipients)
{
var cojectUser = await userManager.FindByNameAsync(user.Name);
if (cojectUser != null)
{
if (cojectUser.Notifications == null)
{
cojectUser.Notifications = new List<Notification>();
}
cojectUser.Notifications.Add(new Notification
{
Message = message.Information,
Seen = false
});
IdentityResult result = await userManager.UpdateAsync(cojectUser);
if (!result.Succeeded)
{
throw new UserUpdateFailException();
}
}
}
}
}
}
I am able to save the custom data to the database, but I am unable to load it again from database.
When I want to display the user's notifications userManager retrieves an user object with null as notification list. Even though the data is stored in database.
public async Task<IActionResult> Index()
{
CojectUser user = await userManager.GetUserAsync(User);
if(user.Notifications == null)
{
user.Notifications = new List<Notification>();
}
return View(user);
}
Data in database:
Can anybody tell me what I am doing wrong?
UserManager don't eager load properties by default.
You should use DatabaseContext directly.
var user = _context.Users.Include(c => c.Notifications).Where(u => u.Id == user.Id).ToList();

Store additional info when creating new user in IdentityStore in web api

I am building/learning token based authentication with OWIN and I would like to figure out how to insert additional information when creating a new user. The UserManager accepts IdentityUser, but the CreateAsync method only accepts a user name and passowrd. I would like to add at least the email address. I see that there is a SetEmailAsync method, but that requires a second call. I feel like there should be a single call that allows me to insert other columns, but I am not finding any documentation of how to do this, nor closely related questions in StackOverflow.
Here is the save routine:
public class AuthRepository : IDisposable
{
private readonly AuthContext _context;
private readonly UserManager<IdentityUser> _userManager;
public AuthRepository()
{
_context = new AuthContext();
_userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_context));
}
public async Task<IdentityUser> FindUserAsync(string userName, string password)
{
IdentityUser user = await _userManager.FindAsync(userName, password);
return user;
}
public async Task<IdentityResult> RegisterUserAsync(UserModel userModel)
{
var user = new IdentityUser
{
UserName = userModel.UserName
};
//save all of this in one call?
var result = await _userManager.CreateAsync(user, userModel.Password);
var result1 = await _userManager.SetEmailAsync(userModel.UserName, userModel.EmailAddress);
return result;
}
public async Task<IdentityUser> FindIdentityUserAsync(string userName, string password)
{
var user = await _userManager.FindAsync(userName, password);
return user;
}
public void Dispose()
{
_context.Dispose();
_userManager.Dispose();
}
}
you can create your own User class by inheriting IdentityUser class.
public class User : IdentityUser
{
public string Email { get; set; }
}
var user = new User
{
UserName = userModel.UserName,
Email = userModel.EmailAddress
};
var result = await _userManager.CreateAsync(user, userModel.Password);
Make sure you are using User Class instead of IdentityUser.

Customising the OWIN/Katana UserManager factory behaviour

There are many samples online using OWIN/Katana to find users in a database based on ausername/password combination and generate a claims principal, such as...
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
// generate claims here...
That's fine if you're creating a new application and want Entity Framework to do the dirty work. But, I have an eight year old monolithic web site that has just been updated to use claims-based authentication. Our database hit is done manually via DAL/SQL and then the ClaimsIdentity is generated from there.
Some people are suggesting that OWIN is easier to use than our manual approach, but I'd like some input from those that use it.
Is it possible to alter how the UserManager factory finds users based on their credentials? Or, is there another approach that I've missed? All the samples I can find online seem to use a boilerplate approach of letting Entity Framework create the database and manage the searches.
ASP.NET Identity is a little bit overly complex, I would say.
In August 2014 they've announced the new version 2.1 and things have changed again.
First of all let's get rid of EntityFramework:
Uninstall-Package Microsoft.AspNet.Identity.EntityFramework
Now we implement our own definition of User implementing the interface IUser (Microsoft.AspNet.Identity):
public class User: IUser<int>
{
public User()
{
this.Roles = new List<string>();
this.Claims = new List<UserClaim>();
}
public User(string userName)
: this()
{
this.UserName = userName;
}
public User(int id, string userName): this()
{
this.Id = Id;
this.UserName = userName;
}
public int Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
public bool LockoutEnabled { get; set; }
public DateTime? LockoutEndDateUtc { get; set; }
public bool TwoFactorEnabled { get; set; }
public IList<string> Roles { get; private set; }
public IList<UserClaim> Claims { get; private set; }
}
As you can see I have defined the type of my Id (int).
Then you have to define your custom UserManager inheriting from Microsoft.AspNet.Identity.UserManager specifying the your user type and the key type.
public class UserManager : UserManager<User, int>
{
public UserManager(IUserStore<User, int> store): base(store)
{
this.UserLockoutEnabledByDefault = false;
// this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10);
// this.MaxFailedAccessAttemptsBeforeLockout = 10;
this.UserValidator = new UserValidator<User, int>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
// Configure validation logic for passwords
this.PasswordValidator = new PasswordValidator
{
RequiredLength = 4,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
}
}
I've implemented my validation rules here but you can keep it outside if you prefer.
UserManager needs a UserStore (IUserStore).
You will define your DB logic here. There are a few interfaces to implement. Not all of them are mandatory though.
public class UserStore :
IUserStore<User, int>,
IUserPasswordStore<User, int>,
IUserLockoutStore<User, int>,
IUserTwoFactorStore<User, int>,
IUserRoleStore<User, int>,
IUserClaimStore<User, int>
{
// You can inject connection string or db session
public UserStore()
{
}
}
I haven't included all the methods for each interface. Once you have done that you'll be able to write your new user:
public System.Threading.Tasks.Task CreateAsync(User user)
{
}
fetch it by Id:
public System.Threading.Tasks.Task<User> FindByIdAsync(int userId)
{
}
and so on.
Then you'll need to define your SignInManager inheriting from Microsoft.AspNet.Identity.Owin.SignInManager.
public class SignInManager: SignInManager<User, int>
{
public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager): base(userManager, authenticationManager)
{
}
public override Task SignInAsync(User user, bool isPersistent, bool rememberBrowser)
{
return base.SignInAsync(user, isPersistent, rememberBrowser);
}
}
I've only implemented SignInAsync: it will generates a ClaimsIdentity.
That's pretty much it.
Now in your Startup class you have to tell Owin how to create the UserManager and the SignInManager.
app.CreatePerOwinContext<Custom.Identity.UserManager>(() => new Custom.Identity.UserManager(new Custom.Identity.UserStore()));
// app.CreatePerOwinContext<Custom.Identity.RoleManager>(() => new Custom.Identity.RoleManager(new Custom.Identity.RoleStore()));
app.CreatePerOwinContext<Custom.Identity.SignInService>((options, context) => new Custom.Identity.SignInService(context.GetUserManager<Custom.Identity.UserManager>(), context.Authentication));
I haven't used the factories you will find in the default template cause I wanted to keep things as simple as possible.
And enable your application to use the cookie:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<Custom.Identity.UserManager, Custom.Identity.User, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) =>
{
var userIdentity = manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
return (userIdentity);
},
getUserIdCallback: (id) => (Int32.Parse(id.GetUserId()))
)}
});
Now in your account controller - or the controller responsible for the login - you will have to get the UserManager and the SignInManager:
public Custom.Identity.SignInManager SignInManager
{
get
{
return HttpContext.GetOwinContext().Get<Custom.Identity.SignInManager>();
}
}
public Custom.Identity.UserManager UserManager
{
get
{
return HttpContext.GetOwinContext().GetUserManager<Custom.Identity.UserManager>();
}
}
You will use the SignInManager for the login:
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
and the UserManager to create the user, add roles and claims:
if (ModelState.IsValid)
{
var user = new Custom.Identity.User() { UserName = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// await UserManager.AddToRoleAsync(user.Id, "Administrators");
// await UserManager.AddClaimAsync(user.Id, new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Country, "England"));
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
It seems complicate ... and it is ... kind of.
If you want to read more about it there's a good explanation here and here.
If you want to run some code and see how it works, I've put together some code which works with Biggy (as I didn't want to waste to much time defining tables and stuff like that).
If you have the chance to download my code from the github repo, you'll notice that I have created a secondary project (Custom.Identity) where I've kept all my ASP.NET Identity stuff.
The only nuget packages you will need there are:
Microsoft.AspNet.Identity.Core
Microsoft.AspNet.Identity.Owin

a request level singleton object in asp.net

I trying to write a kind of pseudo singleton implementation. I want it to work similar to how HttpContext does work, where I can get an instance to the context doing something as simple as:
var ctx = HttpContext.Current;
So my implementation goes something like this:
public class AppUser
{
public string Username { get; set; }
public string[] Roles { get; set; }
public AppUser()
{
var appuser = HttpContext.Session["AppUser"] as AppUser;
if(appuser == null)
throw new Exception("User session has expired");
Username = appuser.Username;
Roles = appuser.Roles;
}
}
public class WebAppContext
{
const string ContextKey = "WebAppContext";
WebAppContext() { } //empty constructor
public static WebAppContext Current
{
get
{
var ctx = HttpContext.Current.Items[ContextKey] as WebAppContext;
if(ctx == null)
{
try
{
ctx = new WebAppContext() { User = new AppUser() };
}
catch
{
//Redirect for login
}
HttpContext.Current.Items.Add(ContextKey, ctx);
}
return ctx;
}
}
public AppUser User { get; set; }
}
And I try to consume this object as follows:
var appuser = WebAppContext.Current.User;
Now does the above line guarantee I get the user associated with the correct request context; not some other user which is associated with another concurrent http request being processed?
Apart from the fact that I can't understand why would you need to barely copy the user information from the Session container to the Items container, the answer to your question should be - yes, if the Session data is correct then the same data will be available from your static property.
I wrote a blog entry on that once
http://netpl.blogspot.com/2010/12/container-based-pseudosingletons-in.html

How to use new MVC5 Authentication with existing database

I've looked through the current literature but I'm struggling to workout exactly how to make the new IdentityStore system work with your own database.
My database's User table is called tblMember an example class below.
public partial class tblMember
{
public int id { get; set; }
public string membership_id { get; set; }
public string password { get; set; }
....other fields
}
currently users login with the membership_id which is unique and then I use the id throughout the system which is the primary key. I cannot use a username scenario for login as its not unique enough on this system.
With the examples I've seen it looks like the system is designed to me quite malleable, but i cannot currently workout how to get the local login to use my tblmember table to authenticate using membership_id and then I will have access the that users tblMember record from any of the controllers via the User property.
http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc-5.aspx
Assuming you are using EF, you should be able to do something like this:
public partial class tblMember : IUserSecret
{
public int id { get; set; }
public string membership_id { get; set; }
public string password { get; set; }
....other fields
/// <summary>
/// Username
/// </summary>
string UserName { get { return membership_id; set { membership_id = value; }
/// <summary>
/// Opaque string to validate the user, i.e. password
/// </summary>
string Secret { get { return password; } set { password = value; } }
}
Basically the local password store is called the IUserSecretStore in the new system. You should be able to plug in your entity type into the AccountController constructor like so assuming you implemented everything correctly:
public AccountController()
{
var db = new IdentityDbContext<User, UserClaim, tblMember, UserLogin, Role, UserRole>();
StoreManager = new IdentityStoreManager(new IdentityStoreContext(db));
}
Note the User property will contain the user's claims, and the NameIdentifier claim will map to the IUser.Id property in the Identity system. That is not directly tied to the IUserSecret which is just a username/secret store. The system models a local password as a local login with providerKey = username, and loginProvider = "Local"
Edit: Adding an example of a Custom User as well
public class CustomUser : User {
public string CustomProperty { get; set; }
}
public class CustomUserContext : IdentityStoreContext {
public CustomUserContext(DbContext db) : base(db) {
Users = new UserStore<CustomUser>(db);
}
}
[TestMethod]
public async Task IdentityStoreManagerWithCustomUserTest() {
var db = new IdentityDbContext<CustomUser, UserClaim, UserSecret, UserLogin, Role, UserRole>();
var manager = new IdentityStoreManager(new CustomUserContext(db));
var user = new CustomUser() { UserName = "Custom", CustomProperty = "Foo" };
string pwd = "password";
UnitTestHelper.IsSuccess(await manager.CreateLocalUserAsync(user, pwd));
Assert.IsTrue(await manager.ValidateLocalLoginAsync(user.UserName, pwd));
CustomUser fetch = await manager.Context.Users.FindAsync(user.Id) as CustomUser;
Assert.IsNotNull(fetch);
Assert.AreEqual("Custom", fetch.UserName);
Assert.AreEqual("Foo", fetch.CustomProperty);
}
EDIT #2: There's also a bug in the implementation of IdentityAuthenticationmanager.GetUserClaims that is casting to User instead of IUser, so custom users that are not extending from User will not work.
Here's the code that you can use to override:
internal const string IdentityProviderClaimType = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
internal const string DefaultIdentityProviderClaimValue = "ASP.NET Identity";
/// <summary>
/// Return the claims for a user, which will contain the UserIdClaimType, UserNameClaimType, a claim representing each Role
/// and any claims specified in the UserClaims
/// </summary>
public override async Task<IList<Claim>> GetUserIdentityClaims(string userId, IEnumerable<Claim> claims) {
List<Claim> newClaims = new List<Claim>();
User user = await StoreManager.Context.Users.Find(userId) as IUser;
if (user != null) {
bool foundIdentityProviderClaim = false;
if (claims != null) {
// Strip out any existing name/nameid claims that may have already been set by external identities
foreach (var c in claims) {
if (!foundIdentityProviderClaim && c.Type == IdentityProviderClaimType) {
foundIdentityProviderClaim = true;
}
if (c.Type != ClaimTypes.Name &&
c.Type != ClaimTypes.NameIdentifier) {
newClaims.Add(c);
}
}
}
newClaims.Add(new Claim(UserIdClaimType, userId, ClaimValueTypes.String, ClaimsIssuer));
newClaims.Add(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String, ClaimsIssuer));
if (!foundIdentityProviderClaim) {
newClaims.Add(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String, ClaimsIssuer));
}
var roles = await StoreManager.Context.Roles.GetRolesForUser(userId);
foreach (string role in roles) {
newClaims.Add(new Claim(RoleClaimType, role, ClaimValueTypes.String, ClaimsIssuer));
}
IEnumerable<IUserClaim> userClaims = await StoreManager.Context.UserClaims.GetUserClaims(userId);
foreach (IUserClaim uc in userClaims) {
newClaims.Add(new Claim(uc.ClaimType, uc.ClaimValue, ClaimValueTypes.String, ClaimsIssuer));
}
}
return newClaims;
}

Resources