这是一个答案,但我确信它最终不会是最好的答案:
这是可以做到的,但是很难看。
首先,您需要创建一个包含您将要使用的所有泛型的类,只是为了让您的生活更轻松。那些是:
[Table("AspNetUserRoles")]
public class StandardUserRole : IdentityUserRole<string>
[Table("AspNetRoles")]
public class StandardRole : IdentityRole<string, StandardUserRole>
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
(以上超类可以在Microsoft.AspNet.Identity.EntityFramework
).
这将使以下通用定义变得更短,并且更难进入由于书写错误而无法编译的地方。
当您在这里时,也可以将这些添加到 DbContext,这通常不会让您可以使用它们:
public DbSet<LoginIdentity> LoginIdentities { get; set; }
public DbSet<StandardUserRole> UserRoles { get; set; }
现在,疯狂的事情来了:
public class Db :
// Replace this with a custom implementation
//IdentityDbContext<Visitor>,
IdentityDbContext<Visitor, StandardRole, string, LoginIdentity,
StandardUserRole, IdentityUserClaim>,
并且,访问者将需要自己进行调整以匹配此声明:
public class Visitor : IdentityUser<string, LoginIdentity, StandardUserRole,
IdentityUserClaim>
这满足了模型(顺便说一句,出于迁移性能原因,最好将其放在自己的项目中)。但是,您仍然需要处理所有 Identity/OWIN 问题。
默认情况下,您会获得一个涉及 UserStore 的 ApplicationUserManager。它通常继承自 UserManager,但现在限制太多了 - 您需要稍微扩展它:
public class VisitorManager : UserManager<Visitor, string>
{
public VisitorManager(IUserStore<Visitor, string> store)
: base(store)
{
}
public static VisitorManager Create(
IdentityFactoryOptions<VisitorManager> options,
IOwinContext context)
{
var manager = new VisitorManager(new UserStore<Visitor,
StandardRole, string, LoginIdentity, StandardUserRole,
IdentityUserClaim>(context.Get<Db>()));
我警告过你要疯狂。登录管理器:
public class SignInManager : SignInManager<Visitor, string>
{
public SignInManager(VisitorManager userManager,
IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(
Visitor user)
{
return user.GenerateUserIdentityAsync((VisitorManager)UserManager);
}
public static SignInManager Create(
IdentityFactoryOptions<SignInManager> options, IOwinContext context)
{
return new SignInManager(context.GetUserManager<VisitorManager>(),
context.Authentication);
}
}
这应该可以帮助你完成大部分肮脏的工作。不容易。但是,完成此操作后,您就获得了一个有效的实现,您可以在其中向登录表添加额外的字段!您现在可以扩展 OWIN Auth 内容来提供事件,并侦听新登录的创建。然后,您可以通过添加额外信息来回复这些信息。
在我们的例子中,目标是在单个用户/访问者帐户上跨多个电子邮件地址从多个 OpenId/OAuth 提供商(Google、Facebook 等)进行多次登录。该框架确实支持这一点,但是,它不会记录哪些电子邮件与哪些登录行相关联,这在将更多登录名与给定帐户合并时非常重要。
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
{
/// <summary>
/// The email address associated with this identity at this provider
/// </summary>
[MaxLength(300)]
public string Email { get; set; }
}
要使整个事情正常运行,您还需要做更多的事情,但从上面的起点来看,它应该相对明显 - 但有一个例外,我将在这里指出。
通过迁移自UserManager<TVisitor>
to UserManager<TVisitor, string>
,您会悄悄地失去前者内置的 ID 生成功能。你需要自己模仿它。作为另一个陷阱,一路上你很可能会实现Visitor
as IUser<string>
。这样做将阻止您设置 Id 属性,因为它是只读的(无设置器)。您可以使用第二个接口来避免这种情况:
public interface IVisitor
{
string Id { get; set; }
string Uid { get; set; }
string UserName { get; set; }
string Email { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
ICollection<StandardUserRole> Roles { get; }
ICollection<LoginIdentity> Logins { get; }
}
有了它,你就可以安全地设置 Id (即使在抽象类中):
public override Task<IdentityResult> CreateAsync(Visitor user)
{
var guid = Guid.NewGuid();
string id = guid.ToString();
((IVisitor)user).Id = id;
return base.CreateAsync(user);
}
请记住对 CreateAsync(访问者用户,字符串密码)执行相同的操作。否则创建的用户会爆炸DbEntityValidationException
投诉 ID 为必填字段。