Aplicação MVC5:Criar resultados de usuário de identidade em estado de modelo inválido?
-
21-12-2019 - |
Pergunta
Estou atualizando um aplicativo em Desenvolvimento de MVC4/EF5 para MVC5/EF6 para fazer uso (entre outras coisas) do ASP.Net Identity.Quando tento criar um usuário, meu código sinaliza o modelo como inválido e não cria o usuário.My View simplesmente exibe uma caixa para inserir um e-mail e, em seguida, um switch que permite ao administrador conectado selecionar uma MemberOrganization ou Sponsor para atribuir o novo usuário 2 por meio de alguns menus suspensos.
O método Create() do meu UserController está abaixo:
// GET: Admin/UserManagement/Create
public ActionResult Create()
{
ViewBag.headerTitle = "Create User";
ViewData["Organization"] = new SelectList(db.MemberOrganizations, "Id", "Name");
ViewData["Sponsor"] = new SelectList(db.SponsorOrganizations, "Id", "Name");
ViewBag.SwitchState = true;
ApplicationUser newUser = new ApplicationUser();
newUser.RegisteredDate = DateTime.Now;
newUser.LastVisitDate = DateTime.Now;
newUser.ProfilePictureSrc = null;
return View(newUser);
}
// POST: Admin/UserManagement/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "Property1, Property2, etc.")] ApplicationUser applicationUser)
{
if (ModelState.IsValid)
{
ViewBag.headerTitle = "Create User";
PasswordHasher ph = new PasswordHasher();
var password = ph.HashPassword("aR@nD0MP@s$w0r9");
var user = new ApplicationUser() { UserName = applicationUser.UserName, Email = applicationUser.Email, PasswordHash = password };
IdentityResult result = await UserManager.CreateAsync(user, user.PasswordHash);
if (result.Succeeded)
{
await db.SaveChangesAsync();
return RedirectToAction("Index", "UserManagement");
}
else
{
ModelState.AddModelError("", "Failed to Create User.");
}
}
ModelState.AddModelError("", "Failed to Create User.");
var errors = ModelState.Where(x => x.Value.Errors.Count > 0).Select(x => new { x.Key, x.Value.Errors }).ToArray();
var errors2 = ModelState.Values.SelectMany(v => v.Errors);
ViewData["Organization"] = new SelectList(db.MemberOrganizations, "Id", "Name", applicationUser.MemberOrgId);
ViewData["Sponsor"] = new SelectList(db.SponsorOrganizations, "Id", "Name", applicationUser.SponsorOrgId);
if (applicationUser.MemberOrgId != null)
{
ViewBag.SwitchState = true;
}
else
{
ViewBag.SwitchState = false;
}
ViewBag.OrganizationId = new SelectList(db.MemberOrganizations, "Id", "State", applicationUser.MemberOrgId);
// If we got this far, something failed, redisplay form
return View(applicationUser);
}
Em minhas tentativas de depurar o problema, adicionei o errors/errors2
variáveis, conforme sugerido em esse publicar.Indo para as propriedades do estado do modelo quando estas são sinalizadas, recebo:
Alguém tem alguma opinião sobre este assunto?Meu código anterior estava funcionando bem, mas ainda estou me acostumando com o ASP.Net Identity.
EDITAR:Conforme sugerido por Rikard, defini meu modelo onde SponsorOrgID e MemberOrgID não são obrigatórios (apenas 1).Agora meu código é processado até o seguinte segmento:
var user = new ApplicationUser() { Name = applicationUser.Name, Email = applicationUser.Email, PasswordHash = password };
IdentityResult result = await UserManager.CreateAsync(user, user.PasswordHash);
if (result.Succeeded) // ERROR
{
await db.SaveChangesAsync();
return RedirectToAction("Index", "UserManagement");
}
Quando eu verifico o valor de result
e detalhar Errors->[string[]]->[0]
a mensagem de erro é: Name cannot be null or empty
.Alguém tem alguma opinião sobre isso?Adicionei um campo à minha View para especificar os novos usuários Name
e incorporou-o ao acima new ApplicationUser()
linha de código.Não tenho certeza de onde estou faltando alguma coisa.
EDITAR2:Criar() Visualizar [Relevante]:
@model PROJECTS.Models.ApplicationUser
@{
ViewBag.Title = "Create";
Layout = "~/Areas/Admin/Views/Shared/_LayoutAdmin.cshtml";
string cancelEditUrl = "/Admin/UserManagement/";
}
@using (Html.BeginForm("Create", "UserManagement", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.RegisteredDate)
<div class="container">
<div class="row">
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field" style="margin-bottom: 15px">
@Html.TextBoxFor(model => model.Name, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="row">
<div class="editor-label">
@Html.LabelFor(model => model.Email)
</div>
<div class="editor-field" style="margin-bottom: 15px">
@Html.TextBoxFor(model => model.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Email)
</div>
</div>
....
Solução
Como você pode ver na sua última foto você tem um erro na propriedade PatrocinadorOrgId que tem o valor string.Empty ("").Talvez o PatrocinadorOrgId em Usuário do aplicativo tem o [Obrigatório] atributo.
EDITAR
Quanto ao seu problema ao tentar adicionar o usuário ao Banco de Dados (isso acontecia quando você chamava UserManager.Create(user,password);
IdentityResult result = await UserManager.CreateAsync(user, user.PasswordHash);
if (result.Succeeded)
{
await db.SaveChangesAsync();
return RedirectToAction("Index", "UserManagement");
}
else
{
var errors = string.Join(",", result.Errors);
ModelState.AddModelError("", errors);
}
Então você pode depurar o valor de "erros" ou ler a mensagem de erro do seu ModelState.
Em relação à sua EDIÇÃO
Adicione um nome a esta parte:
var user = new ApplicationUser() { UserName = applicationUser.UserName, Email = applicationUser.Email, PasswordHash = password, Name = applicationUser.Name };
EDITAR 2O problema é que não é possível criar um usuário sem nome de usuário.Mas você pode adicionar o e-mail do usuário ao nome de usuário.E então mude para o nome de usuário especificado pelo usuário.Para passar na validação você precisa adicionar esta parte.
UserManager.UserValidator = new UserValidator<User>(UserManager) { RequireUniqueEmail = true };
Outras dicas
Sei que é tarde para uma resposta, mas li quatro tópicos sobre isso antes de resolver o problema.Não é totalmente óbvio e parece ser um conflito de herança com propriedades personalizadas.A raiz do meu problema foi a criação de uma propriedade UserName - uma propriedade personalizada (... ou assim pensei) que eu queria definir como Nome + " " + Sobrenome.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
// public new string UserName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
...remainder removed for clarity.
Assim que comentei esta linha de IdentityModels.cs, que removeu minha propriedade personalizada, POF! Problema:resolvido.Analisei as definições de herança até IUser[TKey] e descobri o que considero ser a raiz do meu (nosso) problema.
namespace Microsoft.AspNet.Identity
{
// Summary:
// Minimal interface for a user with id and username
//
// Type parameters:
// TKey:
public interface IUser<out TKey>
{
// Summary:
// Unique key for the user
TKey Id { get; }
//
// Summary:
// Unique username
string UserName { get; set; }
}
}
Eu sabia que você poderia adicionar propriedades personalizadas à classe ApplicationUser, mas não sabia que já havia nomes de propriedades específicos em uso - não definidos nesta classe.Inicialmente, depois de adicionar meu campo UserName simplesmente como uma string pública, recebi isto:
[...] warning CS0114: 'MyApp.Models.ApplicationUser.UserName' hides inherited member 'Microsoft.AspNet.Identity.EntityFramework.IdentityUser<string,Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin,Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole,Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim>.UserName'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
Sou bom em seguir instruções, então adicionei a palavra-chave 'new' (lembra daquela linha acima que tive que comentar...?).Resolveu o aviso CS0114 do tempo de compilação, mas obstruiu o nome de usuário do IUser.
OP (e inúmeros outros) fizeram a mesma coisa, acredito, quando escreveu isto:
var user = new ApplicationUser() { UserName = applicationUser.UserName, Email = applicationUser.Email, PasswordHash = password };
Se você tiver propriedades de nome de usuário e email em sua classe applicationuser, essas propriedades estão ocultando propriedades reais, portanto, remova-as de sua classe de aplicativo. Isso resolverá o problema.