Delete user in ASP.NET Identity and Entity Framework - entity-framework

I need to delete a user, his roles and the UserTitle (many-to-many) entities for the user. I want to do this in one transaction, the db context and userManager are injected into controller. Do I need to use SaveChangesAsync in this scenario, or is transaction.Commit enough to push changes to the database?
var userTitles = db.UserTitles
.Where(ut => ut.UserId == user.Id);
using (var transaction = db.Database.BeginTransaction())
{
foreach (var userTitle in userTitles.ToList())
db.UserTitles.Remove(userTitle);
foreach (var item in rolesForUser.ToList())
await userManager.RemoveFromRoleAsync(user, item);
await userManager.DeleteAsync(user);
transaction.Commit();
await db.SaveChangesAsync();
}

Related

EFCore.BulkExtensions.BulkInsertAsync giving issues when I try create a Identity user

I am busy merging two projects and with that there are certain methods that I now have to use. When I am creating a new user it fails complaining about Primary key constrains. The weird thing is that the primary key is in a different table from the one that it is complaining about. So my thinking is that it has to do with EFCore.BulkExtensions.BulkInsertAsync(Third party tool by borisdj) and how it saves and UserManager.CreateAsync and how it saves.
This is the error that I am getting:
Innermost exception Microsoft.Data.SqlClient.SqlException :
Violation of PRIMARY KEY constraint 'PK_dbo.UserDetail'. Cannot insert
duplicate key in object 'dbo.UserDetail'.
I have a method with this if statement:
if (await CreateUser.CreateUserDetailAsync(model))
{
await CreateAccountUser(model.Email, model.Password, model.Role, model.Fullname);
return RedirectToAction("Index", "ShowUser");
}
I get the error in the CreateAccountUser user method but it complains about the table in CreateUser.CreateUserDetailAsync.
Th error complains about my UserDetail here is how this is saved. This part works because even with the error, the data get saved to the database.
private async Task<boolean> CreateUserDetailAsync(_Models.Settings.User user)
{
var userDetail = new UserDetail
{
Code = user.ERPCode,
Email = user.Email,
Name = user.Name,
Surname = user.Surname
};
//Create the
await UserDetail.BulkInsertAsync(new List<UserDetail>{userDetail});
return true;
}
This is my method for bulk inserts using EFCore.BulkExtensions.BulkInsertAsync:
public Task BulkInsertAsync(IEnumerable<T> entities)
{
if (entities == null)
throw new ArgumentNullException("entity");
foreach (var item in entities)
{
//Attempt to set the Create value of the entity
var created = typeof(T).GetProperty("Created");
if (created == null) created = typeof(T).GetProperty("CreatedOn");
created?.SetValue(item, DateTime.Now, null);
//Attempt to set the Modified value of the entity
var modified = typeof(T).GetProperty("Modified");
if (modified == null) modified = typeof(T).GetProperty("ModifiedOn");
modified?.SetValue(item, DateTime.Now, null);
Entities.Add(item);
}
return Throw(Db).BulkInsertAsync(entities.ToList());
}
I am including this code just for clarity:
private static DbContext Throw(IDataContext context) => context as DbContext;
Then when I get to this method, it fails on await UserManager.CreateAsync.
private async Task CreateAccountUser(string email, string password, string role,
string userFullName = null)
{
//Register User with identity
var accountUser = new Core.Data.Models.User();
accountUser.Email = accountUser.UserName = email;
var result = await UserManager.CreateAsync(accountUser, password);
if (result.Succeeded)
{
return true;
}
else
{
return true;
}
}
There are no linking between my User and UserDetail, they are completely separate.

EF Core Deleting Entities in Disconnected Environment

I'm having real difficulty with EF Core with a Web API project I'm working... to me EF Core is not intuitive at all. I'm in a disconnected environment and I'm trying to update Sudoku games. EF Core is spending more time deleting connections between users and their apps and roles than in updating the game. How do I disable delete statements in an update? There is no reason for deletes, I don't need them. How do I stop them?
The method is as follows, the game is loaded as a graph and my understanding is this code should change everything tracked to modified or added. To me it seems like EF Core is going out of it's way to delete things... this makes no sense. I never instructed it to delete anything:
async public Task<IRepositoryResponse> Update(TEntity entity)
{
var result = new RepositoryResponse();
try
{
dbSet.Update(entity);
context.ChangeTracker.TrackGraph(entity,
e => {
var dbEntry = (IEntityBase)e.Entry.Entity;
if (dbEntry.Id != 0)
{
e.Entry.State = EntityState.Modified;
}
else
{
e.Entry.State = EntityState.Added;
}
});
await context.SaveChangesAsync();
result.Success = true;
result.Object = entity;
return result;
}
catch (Exception exp)
{
result.Success = false;
result.Exception = exp;
return result;
}
}
Well, I found a work around but it is the equivalent to the fixing your bike with bubblegum and tape. It's ugly... but it works. Before I save the game I create a list of all associated apps and roles and then recreate and resave the values after await context.SaveChangesAsync();. The code is listed below:
async public Task<IRepositoryResponse> Update(TEntity entity)
{
var result = new RepositoryResponse();
try
{
entity.DateUpdated = DateTime.UtcNow;
context.Games.Update(entity);
context.ChangeTracker.TrackGraph(entity,
e => {
var dbEntry = (IEntityBase)e.Entry.Entity;
if (dbEntry.Id != 0)
{
e.Entry.State = EntityState.Modified;
}
else
{
e.Entry.State = EntityState.Added;
}
});
var apps = new List<App>();
var roles = new List<Role>();
foreach (var userApp in entity.User.Apps)
{
apps.Add(userApp.App);
}
foreach (var userRole in entity.User.Roles)
{
roles.Add(userRole.Role);
}
await context.SaveChangesAsync();
foreach (var app in apps)
{
userAppDbSet.Add(new UserApp(entity.UserId, app.Id));
}
foreach (var role in roles)
{
userRoleDbSet.Add(new UserRole(entity.UserId, role.Id));
}
await context.SaveChangesAsync();
result.Success = true;
result.Object = entity;
return result;
}
catch (Exception exp)
{
result.Success = false;
result.Exception = exp;
return result;
}
}
There has to be a better way of doing this? The full app can be found here, can someone tell me a better way of setting this up:
https://github.com/Joseph-Anthony-King/SudokuCollective

Query client evaluations in Entity Framework Identity

In my Asp .Net Core 3.1 app I'm using EF Core Identity to handle users authentication. As a database provider i use CosmosDB with SQL API.
Creating user with userManager.CreateAsync(...) goes smooth. I see new user data in database.
But when I'm trying to sign in with these credentials using signInManager.PasswordSignInAsync(...) I get exception:
InvalidOperationException: The LINQ expression 'DbSet<IdentityUserRole<string>>
.Join(
outer: DbSet<IdentityRole>,
inner: i => i.RoleId,
outerKeySelector: i0 => i0.Id,
innerKeySelector: (i, i0) => new TransparentIdentifier<IdentityUserRole<string>, IdentityRole>(
Outer = i,
Inner = i0
))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Linked microsoft document says that this query has to be evaluated on client. So my question is, how can i change query for IdentityRole to achievie that?
Im using default Identity entities - IdentityUser and IdentityRole
Sign up code:
public class DashboardController : Controller
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
public DashboardController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[HttpPost]
public async Task<IActionResult> Register(string username, string password)
{
var user = new IdentityUser
{
UserName = username,
Id = username,
};
var result = await _userManager.CreateAsync(user, password);
if (result.Succeeded)
{
//sign in
var signInResult = await _signInManager.PasswordSignInAsync(user, password, false, false);
// here I also tried to use method with different signature (username, password, ...)
if (signInResult.Succeeded)
{
return RedirectToAction("Index");
}
}
return RedirectToAction("Index");
}
[HttpPost]
public async Task<IActionResult> SignIn(string username, string password)
{
var user = await _userManager.FindByNameAsync(username);
if (user != null)
{
var signInResult = await _signInManager.PasswordSignInAsync(user.UserName, password, false, false);
if (signInResult.Succeeded)
{
return RedirectToAction("Index");
}
}
return RedirectToAction("SignIn");
}
.
.
.
}

Do I really have to update every property individually with Entity Framework?

I have an update method in my Web API repository that looks like this (in EF Core):
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
employee.FirstName = emp.FirstName;
employee.LastName = emp.LastName;
employee.Supervisor = emp.Supervisor;
employee.OfficeBureau = emp.OfficeBureau;
employee.Notes = emp.Notes;
await _context.SaveChangesAsync();
return employee;
}
It works well enough. But do we really have to do this?
I want to do something more like this and update all entity properties in one shot:
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
employee = emp;
_context.Update(employee);
await _context.SaveChangesAsync();
return employee;
}
I shouldn't even need this:
employee = emp;
All I should need is this:
_context.Update(emp);
So EF should say, hey, I need to update an Employee object and I know which one it is by the ID of emp you passed me on update.
But I just can't get it to work.
Does anyone know how to do this or am I really supposed to do it like in the first option?
The answer below from Dmitry is not working.
If I put a break point here:
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
and then try and step though, execution seems to get swallowed up on this line:
_context.Entry(emp).State = EntityState.Modified;
and then this is the response returned:
{}
and the employee in the Database is unchanged.
Also tried this:
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
EntityEntry<Employee> entity = _context.Employees.Attach(emp);
entity.State = EntityState.Modified;
_context.Employees.Update(emp);
await _context.SaveChangesAsync();
return employee;
}
But same thing.
Execution gets swallowed up here:
EntityEntry<Employee> entity = _context.Employees.Attach(emp);
Where is the execution going?
Hard to tell with async sometimes.
I got it to work once like this.
Funny, I got to work right off the bat when I put it in a try/catch.
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
try
{
_context.Employees.Update(emp);
}
catch(Exception ex)
{
throw ex;
}
await _context.SaveChangesAsync();
return emp;
}
But it only worked once.
Now it keeps throwing this exception.
{System.InvalidOperationException: The instance of entity type 'Employee' cannot be tracked because another instance of this type with the same key
is already being tracked.
When adding new entities,
for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type).
If you are explicitly setting key values for new entities,
ensure they do not collide with existing entities or temporary values generated for other new entities.
"When attaching existing entities,
ensure that only one entity instance with a given key value is attached to the context.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, Func2 handleNode)
at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState) at Lerd.Models.Concrete.EmployeeRepository.d__4.MoveNext()}"
How do I get rid of this so dbContext.Update works everytime?
public async Task<Employee> Update(Employee emp)
{
_context.Entry(emp).State = EntityState.Modified;
await _context.SaveChangesAsync();
return employee;
}
I had to change my Repo to be Scoped rather than singleton:
From
services.AddSingleton<IEmployeeRepository, EmployeeRepository>();
to:
services.AddScoped<IEmployeeRepository, EmployeeRepository>();

RoleStore and Role management in Asp.NET Identity Without Entity Framework

I am writing WCF Services, I authenticate User who will have access to my WCF Services through ASP.NET Identity without Entity Framework. Now I got an issue on Role Authorization. I am using custom way without Entity Framework so for it to achieve authentication I created User class and UserStore Class. And how could I authorize the role?
[Note:I have Role in Database table (ASPNetRoles and ASPNetUserRoles) that can only access WCF Services and I know I have to decorate the method with principalpermission.]
namespace CalculatorService
{
public class IdentityValidator : UserNamePasswordValidator
{
public override void Validate(string UserName, string Password)
{
using (var userManager = new UserManager<User>(new UserStore("data=source=pcb-sql01;initial catalog=InsitePCB;integrated security=True;MultipleActiveResultSets=True")))
{
var user = userManager.Find(UserName, Password);
if (user == null)
{
var msg = string.Format("Unknown Username {0} or incorrect password {1}", UserName, Password);
Trace.TraceWarning(msg);
throw new FaultException(msg);
// //the client actually will receive MessageSecurityException. But if I throw MessageSecurityException, the runtime will give FaultException to client without clear message.
}
}
}
}
public class RoleAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
using (var userStore = new UserStore("data source=pcb-sql01;initial catalog=InsitePCB;integrated security=True;MultipleActiveResultSets=True"))
{
using (var userManager = new UserManager<User>(userStore))
{
var identity = operationContext.ServiceSecurityContext.PrimaryIdentity;
var user = userManager.FindByName(identity.Name);
if (user == null)
{
var msg = string.Format("Unknown Username {0} .", user.UserName);
Trace.TraceWarning(msg);
throw new FaultException(msg);
}
//Assign roles to the Principal property for runtime to match with PrincipalPermissionAttributes decorated on the service operation.
var roleNames = userManager.GetRoles(user.Id).ToArray();//users without any role assigned should then call operations not decorated by PrincipalPermissionAttributes
operationContext.ServiceSecurityContext.AuthorizationContext.Properties["Principal"] = new GenericPrincipal(operationContext.ServiceSecurityContext.PrimaryIdentity, roleNames);
return true;
}
}
}
}
}