Я перенес его на .Net 8 — все еще с WebAssembly, но решил переместить многие мои функции «Администратора». обратно на сервер, используя новый режим рендеринга InteractiveServer. Что подойдет и прекрасно работает.
У меня есть куча сервисов, которые взаимодействуют с сервером (стандартные контроллеры), но чтобы это работало корректно И для интерактивного сервера, и для веб-сборки, необходимо было превратить службы в интерфейсы и иметь аналогичные (интерфейсные) службы на сервере, которые обходят контроллер и направляются непосредственно на уровень доступа к данным.
Я сделал эту работу в основном.. Но один из мне нужно было преобразовать приложение DBContext для использования DBContext Factory. Это хорошо описано в различных сообщениях Blazor в этом и других блогах. Чтобы избежать подобных ошибок.
Вторая операция была запущена в этом экземпляре контекста до завершения предыдущей операции. Обычно это вызвано тем, что разные потоки одновременно используют один и тот же экземпляр DbContext.
Теперь я получаю такие ошибки отслеживания ядра EF:
{"Экземпляр типа сущности "ApplicationUser" не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {'Id'} уже отслеживается. При присоединении существующих объектов убедитесь, что что прикреплен только один экземпляр объекта с заданным значением ключа. Рассмотрите возможность использования DbContextOptionsBuilder.EnableSensitiveDataLogging, чтобы увидеть конфликтующие значения ключа».
Странно. дело в том, что я вызываю DAL из службы следующим образом:
Код: Выделить всё
public async Task UpdateUser(ApplicationUserDTO user)
{
using var _dbContext = _dbContextFactory.CreateDbContext();
bool updatingcurrrentuser = false;
if (user.Id == CurrentUser.Id) updatingcurrrentuser = true;
InfoBool? result = new();
bool userisAdmin = false;
ApplicationUser? loggedinUser = await _userLib.GetLoggedInUser(_dbContext);
if (_userLib.UserIsAdmin(loggedinUser))
{
userisAdmin = true; // This is to allow amin users to add paper tickets which can override the ticket price check
}
if (userisAdmin || (user.Id == loggedinUser.Id)) // If the logged in user is an admin or the person edited then allow the update
{
//ApplicationUser sentUser = new ApplicationUser();
//_mapper.Map(sentUserDTO, sentUser);
result = await _userLib.UpdateUser(_userManager, _dbContext, user, user.Roles, userisAdmin);
}
... more stuff
Код: Выделить всё
public async Task UpdateUser(
UserManager userManager,
RGDbContext context,
ApplicationUserDTO user,
List Roles = null,
bool userisadmin = false
)
{
//First check if its there
//ApplicationUser founduser = await userManager.FindByNameAsync(user.UserName);
ApplicationUser? founduser = await context.Users.Include(x => x.Address)
//.Include(x => x.Player) // Dont need the player ***
//.ThenInclude(y => y.Coins)
.SingleOrDefaultAsync(x => x.UserName == user.UserName);
if (founduser == null)
{
return new InfoBool(false, "User to update not found");
}
if (founduser.Address != null && user.Address != null && user.Address.Id == 0)
{ // If there is an address but the client sends it with a zero id - to detect a client error
return new InfoBool(false, "Address Id mismatch Error");
}
_mapper.Map(user, founduser);
_mapper.Map(user.Address, founduser.Address);
IList currentroles = await GetRolesAsync(context, founduser.Id);
// First check for changed roles
// Creates a list of the roles in Roles to add not in current roles
List newrolestoadd = Roles.Except(currentroles).ToList();
// Create a list of the roles in current roles to remove not in roles
List rolestoremove = currentroles.Except(Roles).ToList();
if (newrolestoadd.Count() > 0) // There are some
{
// If a player role has been added
if (!currentroles.Contains(Enums.AllowedRoles.Player.ToString())
&& Roles.Contains(Enums.AllowedRoles.Player.ToString()))
{
founduser.IsPlayer = true;
}
if (!userisadmin && newrolestoadd.Contains(Enums.AllowedRoles.Administrator.ToString()))
{ //Just a check that any user adding an adminrole is already an admin
newrolestoadd.Remove(Enums.AllowedRoles.Administrator.ToString()); //Not allowed
//***TODO Add a log record here to indicate a possible security risk
}
await AddUsertoRoles(userManager, founduser, newrolestoadd);
}
// Remove roles
if (rolestoremove.Count() > 0)
{
if (!userisadmin && rolestoremove.Contains(Enums.AllowedRoles.Administrator.ToString()))
{ // Only an admin can remove an admin
rolestoremove.Remove(Enums.AllowedRoles.Administrator.ToString());
}
await RemoveUserFromRoles(userManager, founduser, rolestoremove);
}
// Now check the address (We dont check for changes IN the address, just if it exists.
try
{
context.Entry(founduser).State = EntityState.Modified;
await context.SaveChangesAsync();
}
catch (Exception e)
{
return new InfoBool(false, e.Message);
}
return new InfoBool(true, "User update successful");
}
Код: Выделить всё
private async Task RemoveUserFromRoles(
UserManager userManager,
ApplicationUser User,
IList RolesToRemove
)
{
try
{
IdentityResult result = IdentityResult.Success;
foreach (string role in RolesToRemove)
{
if (await userManager.IsInRoleAsync(User, role))
{
result = await userManager.RemoveFromRoleAsync(User, role);
if (!result.Succeeded) //If not successful the bail with the error
{
return result;
}
}
}
return result;
}
catch (Exception ex)
{
Console.WriteLine( ex.Message );
return IdentityResult.Failed(new IdentityError() { Code="Exception", Description=$"{ex.Message}"});
}
}
Для полноты кода контроллера:
Код: Выделить всё
public async Task UpdateUser(ApplicationUserDTO sentUserDTO)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Get the logged in user.
ApplicationUser loggedinUser = await _userManager.GetUserAsync(User).ConfigureAwait(false);
IList roles = await _userManager.GetRolesAsync(loggedinUser);
bool userisadmin = false;
if (roles.Contains(Enums.AllowedRoles.Administrator.ToString())) userisadmin = true;
if (userisadmin || (sentUserDTO.Id == loggedinUser.Id)) // If the logged in user is an admin or the person edited then allow the update
{
return await _userlib.UpdateUser(_userManager, _context, sentUserDTO, sentUserDTO.Roles, userisadmin);
}
return Unauthorized();
}
Однако Контекст в контроллерах — это просто стандартный, создаваемый в файле program.cs при запуске. Что мне здесь не хватает?
Подробнее здесь: https://stackoverflow.com/questions/791 ... eruse-stra
Мобильная версия