Пользовательское членство с Microsoft.AspNet.Identity - CreateLocalUser не работает
Я пытаюсь реализовать пользовательскую версию новых функций Identity в ASP.NET 4.5 (Microsoft.AspNet.Identity), используя Visual Studio 2013. После многих часов игры с этим я упростил кода, чтобы заставить его работать без ошибок. Я перечислил свой код ниже. При выполнении локальной регистрации создаются таблицы базы данных, но метод CreateLocalUser терпит неудачу. Я надеюсь, что кто-то может помочь мне определить необходимые изменения.
Модели /MembershipModel.cs
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace thePulse.web.Models
{
public class PulseUser : IUser
{
public PulseUser() { }
public PulseUser(string userName)
{
UserName = userName;
}
[Key]
public string Id { get; set; }
[Required]
[StringLength(20)]
public string UserName { get; set; }
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserClaim : IUserClaim
{
public PulseUserClaim() { }
[Key]
public string Key { get; set; }
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class PulseUserSecret : IUserSecret
{
public PulseUserSecret() { }
public PulseUserSecret(string userName, string secret)
{
UserName = userName;
Secret = secret;
}
[Key]
public string UserName { get; set; }
public string Secret { get; set; }
}
public class PulseUserLogin : IUserLogin
{
public PulseUserLogin() { }
public PulseUserLogin(string userId, string loginProvider, string providerKey)
{
LoginProvider = LoginProvider;
ProviderKey = providerKey;
UserId = userId;
}
[Key, Column(Order = 0)]
public string LoginProvider { get; set; }
[Key, Column(Order = 1)]
public string ProviderKey { get; set; }
public string UserId { get; set; }
}
public class PulseRole : IRole
{
public PulseRole() { }
public PulseRole(string roleId)
{
Id = roleId;
}
[Key]
public string Id { get; set; }
}
public class PulseUserRole : IUserRole
{
public PulseUserRole() { }
[Key, Column(Order = 0)]
public string RoleId { get; set; }
[Key, Column(Order = 1)]
public string UserId { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
Users = new UserStore<PulseUser>(db);
Logins = new UserLoginStore<PulseUserLogin>(db);
Roles = new RoleStore<PulseRole, PulseUserRole>(db);
Secrets = new UserSecretStore<PulseUserSecret>(db);
UserClaims = new UserClaimStore<PulseUserClaim>(db);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, PulseUserClaim, PulseUserSecret, PulseUserLogin, PulseRole, PulseUserRole>
{
}
}
Изменения в контроллерах /AccountController.cs
public AccountController()
{
IdentityStore = new IdentityStoreManager(new PulseUserContext(new PulseDbContext()));
AuthenticationManager = new IdentityAuthenticationManager(IdentityStore);
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
try
{
// Create a profile, password, and link the local login before signing in the user
PulseUser user = new PulseUser(model.UserName);
if (await IdentityStore.CreateLocalUser(user, model.Password))
{
await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
}
}
catch (IdentityException e)
{
ModelState.AddModelError("", e.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Как я уже говорил выше, эта реализация не выполняется, когда метод CreateLocalUser выходит из строя (Microsoft.AspNet.Identity.EntityFramework). Я не могу понять, почему.
Ответы
Ответ 1
Проблема здесь в том, что IdentityStoreManager имеет сильную зависимость от стандартной реализации моделей идентификации EF. Например, метод CreateLocalUser создаст объекты UserSecret и UserLogin и сохранит их в хранилищах, которые не будут работать, если хранилище не использует тип модели по умолчанию. Поэтому, если вы настроите тип модели, он не будет работать плавно с IdentityStoreManager.
Поскольку вы только настраиваете модель IUser, я упростил код, чтобы наследовать пользовательский пользователь от пользователя, использующего идентификатор по умолчанию, и повторно использовать другие модели из моделей идентификации EF.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication11.Models
{
public class PulseUser : User
{
public PulseUser() { }
public PulseUser(string userName) : base(userName)
{
}
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
this.Users = new UserStore<PulseUser>(this.DbContext);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
}
}
Приведенный выше код должен работать с версией предварительного просмотра Identity API.
API IdentityStoreManager в предстоящей версии уже знает об этой проблеме и изменил весь код зависимостей не-EF в базовый класс, чтобы вы могли его настроить, наследуя от него. Он должен решить все проблемы здесь. Спасибо.
Ответ 2
PulseUser.Id определяется как строка, но не имеет значения для значения. Вы хотели использовать GUID для идентификатора? Если это так, инициализируйте его в конструкторе.
public PulseUser() : this(String.Empty) { }
public PulseUser(string userName)
{
UserName = userName;
Id = Guid.NewGuid().ToString();
}
Вы также захотите выполнить проверку того, что имя пользователя еще не существует. Посмотрите на переопределение DbEntityValidationResult в PulseDbContext. Сделайте новый проект MVC в VS2013, чтобы увидеть пример.
Ответ 3
Поскольку при переходе на RTM есть много изменений, я обновил шаблон SPA, который использует контроллер WebApi для всего идентификатора и т.д. Это действительно классный шаблон, если вы его не видели.
Я поместил весь свой код здесь:
https://github.com/s093294/aspnet-identity-rtm/tree/master
(Имейте в виду, что это только для вдохновения. Я только заработал и ничего больше. Правильно есть ошибка или два).