I am using ASP.NET Identity (v2) with Entity Framework. I am building a page with several checkboxes, each representing a claim. On a button click ( SavePrivileges
function in my example) I would like to adjust claims of a given user according to the state of my checkboxes, ie remove claims, which are not checked, and add claims, which are checked.
Adding claims is working OK. However, I seem to be unable to remove claims from the user. Upon calling manager.RemoveClaim
I get an InvalidOperationException saying that collection has changed. I actually get this exception even if I do not modify the user.Claims
collection.
Here is my simplified example scenario:
protected void SavePrivileges(string userId)
{
const string claim1Type = "...";
const string claim2Type = "...";
const string claim1Value = "...";
const string claim2Value = "...";
var store = new UserStore<IdentityUser>();
var manager = new UserManager<IdentityUser>(store);
var user = manager.FindById(userId);
// Lists to keep track of changes
var removed = new List<Claim>();
var added = new List<Claim>();
bool claim1Checked = CheckBox1.Checked;
bool claim2Checked = CheckBox2.Checked;
bool claim1Possesed = user.Claims.FirstOrDefault(claim => claim.ClaimValue == claim1Value &&
claim.ClaimType == claim1Type) != null;
bool claim2Possesed = user.Claims.FirstOrDefault(claim => claim.ClaimValue == claim2Value &&
claim.ClaimType == claim2Type) != null;
if (claim1Checked && !claim1Possesed)
{
added.Add(new Claim(claim1Type, claim1Value));
}
else if (!claim1Checked && claim1Possesed)
{
removed.Add(new Claim(claim1Type, claim1Value));
}
// + The same for claim2...
// Remove extra claims
foreach (Claim claim in removed)
{
manager.RemoveClaim(user.Id, claim);
}
// Add new claims
foreach (Claim claim in added)
{
IdentityUserClaim identityUserClaim = new IdentityUserClaim { ClaimType = claim.Type, ClaimValue = claim.Value };
user.Claims.Add(identityUserClaim);
}
// Save the result
var result = manager.Update(user);
if (result.Succeeded)
{
store.Context.SaveChanges();
}
}
Thanks for any help.
I have found a solution eventually, however, it is not ellegant in any way. If anyone comes up with a better solution, I will still be glad to see it.
foreach (Claim claim in removed) {
var claimCollection = store.Context.Entry(user).Collection(u => u.Claims).CurrentValue;
var storedClaim = claimCollection.FirstOrDefault(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value && c.UserId == user.Id);
if (storedClaim != null)
store.Context.Entry(storedClaim).State = EntityState.Deleted;
}
This worked well for me. Hope this helps!
user.Claims.ToList().ForEach(claim => Context.Entry(claim).State =
System.Data.Entity.EntityState.Deleted);
Context.SaveChanges();
I have swapped over to excellent package ElCamino.AspNet.Identity.AzureTable which provides a Azure Table storage based implementation to replace the standard Microsoft.AspNet.Identity.EntityFramework Entity Framework based storage of Identity.
With the ElCamino.AspNet.Identity.AzureTable package installed the normal userManager.RemoveClaimAsync
works as expected. From this, and a look at where the error occurs in the standard package, I suspect it is a bug in Microsoft.AspNet.Identity.EntityFramework.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.