EntityFrameworks inserts already inserted related item - c#

Im trying to create a user, then connect that user to an model called Event.
var user = new ApplicationUser { name = "test", email = "test#test.com" };
var userResult = UserManager.Create(user, "password");
if (userResult.Succeeded)
{
SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: true);
#event.CreatedBy = user;
#event.CreatedById = new Guid(user.Id);
db.Events.Add(#event);
db.SaveChanges();
}
But db.SaveChanges() throws an validation error on the ApplicationUser model: user with the email already exists: test#test.com (even though it didnt exists before this request)
So it would seem as EF is trying to reinserted the already inserted user.
The Event model have these setters/getters
public virtual ApplicationUser CreatedBy { get; set; }
public virtual Guid CreatedById { get; set; }
And it works great if i would get the current singned in user instead of signing up a new one.
I have tried only to set #event.CreatedById and skip #event.CreatedBy, but then EF doesnt save the reference to the database.

This line creates a new user (and probably saves it to your database):
var userResult = UserManager.Create(user, "password");
So you don't have the result you got back from the database (return value of the Add method), which is a proxy that EF knows exists in database. In fact it's a link between the database record and the object you have in memory. Then you have the following lines:
#event.CreatedBy = user;
db.Events.Add(#event);
db.SaveChanges();
You assign the user (which is a simple poco, no connection to EF whatsoever) to your event. When you add the event to the database, EF will try to create a new user again because it's not know yet, but then of course you get an error. You will have to get the user you created from the database and assign that to your event. If you would be able to change your method UserManager.Create so it also accepts a DbContext, you could pass the context, create the user and when you need the user you can use the same context again and use the Find method (which looks for objects in the context in memory, no db call) to find the user you've added.
Edit: Maybe the most simple solution in your case is to attach the user to your context:
var user = new ApplicationUser { name = "test", email = "test#test.com" };
var userResult = UserManager.Create(user, "password");
if (userResult.Succeeded)
{
SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: true);
db.Users.Attach(user);
#event.CreatedBy = user;
#event.CreatedById = new Guid(user.Id);
db.Events.Add(#event);
db.SaveChanges();
}
This basically tells EF that you know the object already exists in the database.

Related

Prevent collision on multiple database calls in Web API

I have a table of random unique number that they served as account Id for a user.
This scenario will use in Register method of Web API to get first unique Number and attached it into a user and after a successful creation, the fetched unique Number will be removed from database:
public async Task<ActionResult> Register([FromBody] RegisterDto model)
{
//get the first unique number from the database
var UniqueNumber = _context.UniqueNumbers.First();
var user = new User
{
UserName = model.Email,
Email = model.Email,
PhoneNumber = model.PhoneNumber,
FirstName = model.FirstName,
LastName = model.LastName,
UserProfile = new UserProfile()
{
AccountNumber = UniqueNumber.Number,
},
};
//creating user
var createResult = await _userManager.CreateAsync(user, model.Password);
if (!createResult.Succeeded) return BadRequest(new { isSucceeded = createResult.Succeeded, errors = createResult.Errors });
//Delete the fetched UniqueId from the database
_context.UniqueNumbers.Remove(UniqueNumber);
_context.SaveChanges();
return Ok(new
{
isSucceeded = true
});
}
My question is how do I prevent collision in multiple calls on API since it may return same unique number for multiple calls?
On EF Core and Microsoft SQL Server you should simply use a SEQUENCE object to generate your keys. See Sequences - EF Core
If really, really want to proceed with your original design you could use FromSql to run a DELETE … OUTPUT, something like:
var UniqueNumber = _context.UniqueNumbers.FromSql("delete top (1) from UniqueNumbers output deleted.*").Single();

How to update password for the existing user record in AspNetUsers table from WebAPI

I am new to Web API ,Here I am working in a token based authentication .
I done all the login and Signup process with AspNetUsers table except the update password functionalities.
Here I have updated the HashedPassword field by passing the new password .When I try to get token for the specific user it returns some error related with passwordHash format because it directly save the new password without hash.
code :
public string updateUserData(string mEmail,string mPassword)
{
if (ModelState.IsValid)
{
var userStore = new UserStore<ApplicationUser>(new ApplicationDbContext());
var manager = new UserManager<ApplicationUser>(userStore);
ApplicationUser AppModel = manager.FindByEmail(mEmail);
AppModel.PasswordHash = mPassword;
manager.Update(AppModel);
return "Success";
}
return "Update Failed";
}
can anyone help me to do this in a proper way .I am new to this if I did any silly mistake I am asking sorry for that.
create a password reset token and then use that to change the password. Example:
var user = await manager.FindByIdAsync(id);
var token = await manager.GeneratePasswordResetTokenAsync(user);
var result = await manager.ResetPasswordAsync(user, token, "NewPassword");

Add user to role where username is email address

The code below is called to assign the current authenticated user to a role named Admin. The user has already been created with a correctly configured email address and I can authenticate with it. However, I want to add the user to a Role using the demo code below.
public ActionResult AddUserToRole()
{
var userId = User.Identity.GetUserId();
using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
IdentityResult result = userManager.AddToRole(userId, "Admin");
}
return RedirectToAction("Index");
}
The user was created using the template code from a new MVC 5 application. The template code calls for an email address for the username. However, the result of the AddToRole line returns result.Succeeded = false with an error of
User name jason#xxxxxxx.xx.xx is invalid, can only contain letters or
digits.
Why does the call to role apply the validator but the call to create a user doesn't? It's all template code.
Now, given that this is all template code, am I missing something or do I need to modify the template to have the user supply a username that isn't an email address.
Many thanks.
I had the same problem. To get around it, add the following line, before calling AddToRole():
userManager.UserValidator = new UserValidator<ApplicationUser>(userManager) { AllowOnlyAlphanumericUserNames = false };
Someone may know a more elegant solution, but this worked for me, so I never investigated further!
HTH

Using OWIN Identity v2 Claims to track custom properties in WebAPI2 app

getting my head wrapped around the new Identity framework and am trying to figure out how best to handle custom user properties. I have tried extending the IdentityUser, which works to store the information, but so far is requiring an additional db call to get the property back out. I am looking at switching to using claims to store/retrieve this information.
First, the specific prop I want to store/retrieve is not unique to an individual user (many to one). Consider grouping users together in a custom Group structure. I want to store the GroupId for use in other related entities.
I am able to store the GroupId (currently using the ClaimTypes.NameIdentifier which I don't think it the correct usage for that type, but...). But, when I go to retrieve that value, the claim type isn't found in the claims collection. It's in the db, so I know it's there. I'm missing something.
FWIW: Since it's WebAPI, I'm not using a traditional sign-in. I'm using token auth.
When I create the user, I have something like:
public async Task<IdentityResult> CreateUserAsync(string email, string password, string groupId)
{
var userId = ObjectId.GenerateNewId(DateTime.UtcNow).ToString(); // yes, it's a NoSQL store
var user = new ApplicationUser
{
Id = userId,
UserName = email
};
var claim = new IdentityUserClaim { ClaimType = ClaimTypes.NameIdentifier, ClaimValue = groupId, UserId = userId, Id = ObjectId.GenerateNewId(DateTime.UtcNow).ToString() };
user.Claims.Add(claim);
var result = await _UserManager.CreateAsync(user, password);
return result;
}
That creates what looks to be an appropriate db entry.
When I retrieve the value, I get null reference errors. Here's that code via an extension method:
public static string GetGroupId(this IIdentity identity)
{
var claimsIdentity = identity as ClaimsIdentity;
return claimsIdentity == null ? "" : claimsIdentity.FindFirst(ClaimTypes.NameIdentifier).Value;
}
The error hits when trying to get Value as the FindFirst is returning a null value.
Any hints or better/best practices here would be appreciated! Honestly, I'd prefer to just store this on the ApplicationUser : IdentityUser object, but I can't find a simple way of retrieving that of User.Identity in my api controller context without an additional call to the db.
Your gut feeling about storing extra data as a claim is correct, but implementation is a bit broken.
I recommend to have your own claim types created for your domain information. Do not reuse claim types provided from framework. Reason for that is ClaimTypes.NameIdentifier represents User.Id.
The framework itself adds standard list of claims to all users:
User.Id => represented as ClaimTypes.NameIdentifier
Username => represented as 'ClaimTypes.Name'
ProviderName => represented as ClaimTypes.ProviderName (not 100% sure about this one); Usually value is "ASP.NET Identity"
SecurityStamp value (not sure what the claim type name for it)
All the roles assigned to the user are stored as ClaimTypes.Role
So in your case you have tried to overwrite claim with value of User.Id which is quite important, I would think -)
Now, let's try to fix your coding problems. When you create a user, you add claims after you have created a user object:
public async Task<IdentityResult> CreateUserAsync(string email, string password, string groupId)
{
var user = new ApplicationUser
{
Id = userId,
UserName = email
};
var userCreateResult = await _UserManager.CreateAsync(user, password);
if(!userCreateResult.IsSuccess)
{
// user creation have failed - need to stop the transaction
return userCreateResult;
}
// better to have a class with constants representing your claim types
var groupIdClaim = new Claim("MyApplication:GroupClaim", ObjectId.GenerateNewId(DateTime.UtcNow).ToString());
// this will save the claim into the database. Next time user logs in, it will be added to Principal.Identity
var claimAddingResult = await _UserManager.AddClaimAsync(userId, groupIdClaim);
return claimAddingResult;
}
As for extension methods I usually work with IPrincipal or ClaimsPrincipal. But IIdentity is also workable. Don't forget you can access ClaimsPrincipal anywhere by calling ClaimsPrincipal.Current.
This is how I usually work with extension methods:
public static string GetGroupId(this ClaimsPrincipal principal)
{
var groupIdClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApplication:GroupClaim");
if (personIdClaim != null)
{
return groupIdClaim.Value;
}
return String.Empty;
}
So in your methods you'd retrieve assigned groupId for the currently logged in user like this:
var groupId = ClaimsPrincipal.Current.GetGroupId();
Hope this clarifies your confusion!

How to create a user and get the newly created ID with ASP.NET Identity

I am new to the ASP.NET Identity framework and am trying to do some things that I used to do in the older FormsAuthentication framework.
What I want to do is allow an administrative user to create a new user using the existing Register view (or similar) from within the app. Once this is complete, I would like to relationally associate that user (possibly using the ID that is generated) to other areas of the system.
How do I get access to the ID that is generated when calling UserManager.CreateAsync()?
EDIT: I am wanting existing users with "administrative" roles to create users from within the system in a User Management area. The answers below so far have explained how to get the ID for the "current" user which is not what I am looking for.
Using the IdentityUser or using a class that inherits from IdentityUser, makes the model having an UserId attribute. Using following code, passing the user to the method, will fill up the Id.
var user = model.GetUser();
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
result = UserManager.AddToRole(user.Id, "User");
The model.GetUser() returns an object of the ApplicationUser or IdentityUser
public ApplicationUser GetUser()
{
var user = new ApplicationUser
{
UserName = UserName,
FirstName = FirstName,
LastName = LastName,
Email = Email,
...
};
return user;
}
The accepted answer was not clear to me so I thought I would share this.
If you look at the source for the IdentityUser class you will find the following.
public class IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string>
{
/// <summary>
/// Constructor which creates a new Guid for the Id
/// </summary>
public IdentityUser()
{
this.Id = Guid.NewGuid().ToString();
}
... code omitted
}
As you can see, when creating a new IdentityUser instance the constructor generates a new GUID and populates the Id field.
This means if you create a new instance of a derived class such as ApplicationUser a new Id will still automatically be generated for you.
So, after you check that the user has been successfully added you can safely use the Id in the ApplicationUser class for your data association.
Example.
var user = new ApplicationUser
{
UserName = "franko",
FirstName = "Frank",
LastName = "Ouimette",
Email = "franko#emailservice.com"
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded){
result = UserManager.AddToRole(user.Id, "User");
// User added successfully, you can safely use the Id now.
var id = user.Id;
}
What worked for me was something slightly different:
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var user = new ApplicationUser() { Email = "informatyka4444#wp.pl", UserName = "informatyka4444#wp.pl" };
var result = manager.Create(user, "TestPass44!");
if (result.Succeeded)
{
string newId = user.Id;
}
Some extension methods have been added to the Identity framework, including this one:
public static string GetUserId(this IIdentity identity);
After performing the sign-in, you should be able to retrieve the ID easily enough:
await SignInAsync(user, isPersistent: false);
var id = this.User.Identity.GetUserId()
This thread is already a few years old, but I encountered this problem and needed to find another solution because I do not use the provided Guids, but a standard integer ID, provided by the database (auto-incremented index).
The problem that I had was that if I created the user with UserManager.CreateAsync, the user object wasn't updated with the ID. I then created a related object for that user, and since the ID wasn't updated, Entity Framework tried to save the user to the database another time which resulted in a primary key violation.
To solve the problem, I needed to read/update the user object like so:
var user = new User
{
UserName = UserName,
FirstName = FirstName,
LastName = LastName,
Email = Email,
...
};
var result = await UserManager.CreateAsync(user, model.Password);
// At this point, the user has been created, but the ID not set
// Read the full user record from the database
user = db.Users.Where(u => u.Email == user.Email).FirstOrDefault();
// At this point, the user has the correct ID set, we are good to go...
In the event for user created, add:
string user = RegisterUser.UserName; or to use later
Session["user"] = RegisterUser.UserName;

Categories