C# 将ASP.NET标识集成到现有DbContext中

C# 将ASP.NET标识集成到现有DbContext中,c#,entity-framework,ef-code-first,entity-framework-6,asp.net-identity,C#,Entity Framework,Ef Code First,Entity Framework 6,Asp.net Identity,我正在VS2013、.NET 4.5.1中的ASP.NET MVC 5项目中工作,该项目首先使用Entity Framework 6代码。我有一个相当大的数据库建立了出来,有些工作(项目约两周前)。我现在想集成用户身份验证,但我不确定如何实现。在花了一天的大部分时间研究之后,我决定尝试一下新的ASP.NET身份框架,而不是编写自定义成员资格或角色提供程序。我所困惑的是如何使它与我现有的数据库/模型一起工作 目前,我有一个名为Employee的对象,它保存基本的员工信息(目前)。在思考了一整天这个

我正在VS2013、.NET 4.5.1中的ASP.NET MVC 5项目中工作,该项目首先使用Entity Framework 6代码。我有一个相当大的数据库建立了出来,有些工作(项目约两周前)。我现在想集成用户身份验证,但我不确定如何实现。在花了一天的大部分时间研究之后,我决定尝试一下新的ASP.NET身份框架,而不是编写自定义成员资格或角色提供程序。我所困惑的是如何使它与我现有的数据库/模型一起工作

目前,我有一个名为
Employee
的对象,它保存基本的员工信息(目前)。在思考了一整天这个问题之后,我决定将身份验证从它分离到一个
用户
对象,这正是Identity想要的。话虽如此,我怎样才能让一切顺利

这是我的
员工
课程:

public class Employee : Person {
    public int EmployeeId { get; set; }
    public byte CompanyId { get; set; }
    public string Name {
        get {
            return String.Format("{0} {1}", this.FirstName, this.LastName);
        }
    }
    public string Password { get; set; }
    public bool IsActive { get; set; }

    public virtual ICollection<Address> Addresses { get; set; }
    public virtual Company Company { get; set; }
    public virtual ICollection<Email> Emails { get; set; }
    public virtual ICollection<Phone> Phones { get; set; }

    public Employee() {
        this.Addresses = new List<Address>();
        this.Emails = new List<Email>();
        this.Phones = new List<Phone>();
    }
}
请看,以获取ASP.NET Identity的数据库上下文如何扩展以包含新表的示例。这可能适合你的情况。下面是通过继承ASP.NET标识上下文来定义新上下文的方法

public class SecurityContext : IdentityDbContext<ApplicationUser>
{
    public SecurityContext()
        : base("SimpleSecurityConnection")
    {
    }


    public DbSet<Resource> Resources { get; set; }
    public DbSet<OperationsToRoles> OperationsToRoles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Configurations.Add(new ResourceConfiguration());
        modelBuilder.Configurations.Add(new OperationsToRolesConfiguration());
    }
}
公共类SecurityContext:IdentityDbContext并对其进行扩展

由于您的Employee类似乎是会员资格的用户配置文件,因此我将考虑对其进行裁剪以适应。基本上,您的Employee类需要从IdentityUser继承,并且您将从Employee中删除Password属性,因为这是在IdentityUser中定义的,框架将在那里查找它。然后,在定义上下文时,您将使用Employee类,因此它看起来像这样

public class DatabaseContext : IdentityDbContext<Employee>
{
  ...
}
公共类数据库上下文:IdentityDbContext { ... }
因此,在花了大约一天的时间反复阅读之后,我最终构建了自己的身份实现。首先,我将现有的
Employee
对象扩展为从
IUser
继承
IUser
是Identity 2.0(当前为alpha)的一部分,它允许将主键类型配置为1.0中默认的
string
以外的其他类型。由于我存储数据的方式,我的实现非常具体。例如,
员工
可以有多个与之相关的
电子邮件
对象,对于我的应用程序,我希望使用电子邮件作为用户名。因此,我只需设置
UserName
属性以返回
员工的工作电子邮件:

public string UserName {
    get {
        if (this.WorkEmail != null) {
            return this.WorkEmail.Address;
        }

        return null;
    }
    set {
        /// This property is non-settable.
    }
}
请注意,既然我不打算对属性使用setter,那么除了将其留空之外,还有没有更干净的方法来淘汰它呢

接下来,我还添加了
PasswordHash
属性。我添加了自己的
角色
对象,继承自
IRole
。最后,
Employee
Role
对象都有一个相互链接的
ICollection
。另一方面,Identity的实体框架实现手动创建映射表
UserRoles
,而不是利用其自身的配置功能,我似乎无法理解其背后的原因。它创建的
UserRole
确实会被传递到它实现的
*存储中,但除了充当链接之外,它实际上没有做任何特殊的事情。在我的实现中,我只是使用了已经建立的链接,它当然会在数据库中创建一个映射表,但不会毫无意义地暴露在应用程序中。我只是觉得奇怪

接着,我继续使用配置的对象,实现了自己的
IUserStore
IRoleStore
类,这些类被创造性地称为
EmployeeStore
RoleStore

public class EmployeeStore : IQueryableUserStore<Employee, int>, IUserStore<Employee, int>, IUserPasswordStore<Employee, int>, IUserRoleStore<Employee, int>, IDisposable {
    private bool Disposed;
    private IDatabaseRepository<Role> RolesRepository { get; set; }
    private IDatabaseRepository<Employee> EmployeesRepository { get; set; }

    public EmployeeStore(
        IDatabaseRepository<Role> rolesRepository,
        IDatabaseRepository<Employee> employeesRepository) {
        this.RolesRepository = rolesRepository;
        this.EmployeesRepository = employeesRepository;
    }

    #region IQueryableUserStore Members
    public IQueryable<Employee> Users {
        get {
            return this.EmployeesRepository.Set;
        }
    }
    #endregion

    #region IUserStore Members
    public async Task CreateAsync(
        Employee employee) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        await this.EmployeesRepository.AddAndCommitAsync(employee);
    }

    public async Task DeleteAsync(
        Employee employee) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        await this.EmployeesRepository.RemoveAndCommitAsync(employee);
    }

    public Task<Employee> FindByIdAsync(
        int employeeId) {
        this.ThrowIfDisposed();

        return Task.FromResult<Employee>(this.EmployeesRepository.FindSingleOrDefault(
            u =>
                (u.Id == employeeId)));
    }

    public Task<Employee> FindByNameAsync(
        string userName) {
        this.ThrowIfDisposed();

        return Task.FromResult<Employee>(this.EmployeesRepository.FindSingleOrDefault(
            e =>
                (e.UserName == userName)));
    }

    public async Task UpdateAsync(
        Employee employee) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        await this.EmployeesRepository.CommitAsync();
    }
    #endregion

    #region IDisposable Members
    public void Dispose() {
        this.Dispose(true);

        GC.SuppressFinalize(this);
    }

    protected void Dispose(
        bool disposing) {
        this.Disposed = true;
    }

    private void ThrowIfDisposed() {
        if (this.Disposed) {
            throw new ObjectDisposedException(base.GetType().Name);
        }
    }
    #endregion

    #region IUserPasswordStore Members
    public Task<string> GetPasswordHashAsync(
        Employee employee) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        return Task.FromResult<string>(employee.PasswordHash);
    }

    public Task<bool> HasPasswordAsync(
        Employee employee) {
        return Task.FromResult<bool>(!String.IsNullOrEmpty(employee.PasswordHash));
    }

    public Task SetPasswordHashAsync(
        Employee employee,
        string passwordHash) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        employee.PasswordHash = passwordHash;

        return Task.FromResult<int>(0);
    }
    #endregion

    #region IUserRoleStore Members
    public Task AddToRoleAsync(
        Employee employee,
        string roleName) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        if (String.IsNullOrEmpty(roleName)) {
            throw new ArgumentNullException("roleName");
        }

        Role role = this.RolesRepository.FindSingleOrDefault(
            r =>
                (r.Name == roleName));

        if (role == null) {
            throw new InvalidOperationException("Role not found");
        }

        employee.Roles.Add(role);

        return Task.FromResult<int>(0);
    }

    public Task<IList<string>> GetRolesAsync(
        Employee employee) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        return Task.FromResult<IList<string>>(employee.Roles.Select(
            r =>
                r.Name).ToList());
    }

    public Task<bool> IsInRoleAsync(
        Employee employee,
        string roleName) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        if (String.IsNullOrEmpty(roleName)) {
            throw new ArgumentNullException("roleName");
        }

        return Task.FromResult<bool>(employee.Roles.Any(
            r =>
                (r.Name == roleName)));
    }

    public Task RemoveFromRoleAsync(
        Employee employee,
        string roleName) {
        this.ThrowIfDisposed();

        if (employee == null) {
            throw new ArgumentNullException("employee");
        }

        if (String.IsNullOrEmpty(roleName)) {
            throw new ArgumentNullException("roleName");
        }

        Role role = this.RolesRepository.FindSingleOrDefault(
            r =>
                (r.Name == roleName));

        if (role == null) {
            throw new InvalidOperationException("Role is null");
        }

        employee.Roles.Remove(role);

        return Task.FromResult<int>(0);
    }
    #endregion
}
public class RoleStore : IQueryableRoleStore<Role, int>, IRoleStore<Role, int>, IDisposable {
    private bool Disposed;
    private IDatabaseRepository<Role> RolesRepository { get; set; }

    public RoleStore(
        IDatabaseRepository<Role> rolesRepository) {
        this.RolesRepository = rolesRepository;
    }

    #region IQueryableRoleStore Members
    public IQueryable<Role> Roles {
        get {
            return this.RolesRepository.Set;
        }
    }
    #endregion

    #region IRoleStore Members
    public async Task CreateAsync(
        Role role) {
        this.ThrowIfDisposed();

        if (role == null) {
            throw new ArgumentNullException("role");
        }

        await this.RolesRepository.AddAndCommitAsync(role);
    }

    public async Task DeleteAsync(
        Role role) {
        this.ThrowIfDisposed();

        if (role == null) {
            throw new ArgumentNullException("role");
        }

        await this.RolesRepository.RemoveAndCommitAsync(role);
    }

    public Task<Role> FindByIdAsync(
        int roleId) {
        this.ThrowIfDisposed();

        return Task.FromResult<Role>(this.RolesRepository.FindSingleOrDefault(
            r =>
                (r.Id == roleId)));
    }

    public Task<Role> FindByNameAsync(
        string roleName) {
        this.ThrowIfDisposed();

        return Task.FromResult<Role>(this.RolesRepository.FindSingleOrDefault(
            r =>
                (r.Name == roleName)));
    }

    public async Task UpdateAsync(
        Role role) {
        this.ThrowIfDisposed();

        if (role == null) {
            throw new ArgumentNullException("role");
        }

        await this.RolesRepository.CommitAsync();
    }
    #endregion

    #region IDisposable Members
    public void Dispose() {
        this.Dispose(true);

        GC.SuppressFinalize(this);
    }

    protected void Dispose(
        bool disposing) {
        this.Disposed = true;
    }

    private void ThrowIfDisposed() {
        if (this.Disposed) {
            throw new ObjectDisposedException(base.GetType().Name);
        }
    }
    #endregion
}
现在,我注意到实体框架实现正在创建一个看起来像迷你存储库的东西。因为我的项目已经在使用我自己的存储库实现,所以我决定改用它。我们来看看情况如何

现在,所有这些都起作用了,令人惊讶的是根本没有崩溃,或者至少还没有崩溃。话虽如此,我拥有所有这些出色的身份实现,但我似乎不知道如何在我的MVC应用程序中利用它们。因为这超出了这个问题的范围,我将继续并打开一个新的解决方案


我留下这个作为问题的答案,以防将来有人碰到这个问题。当然,如果有人看到我发布的代码中有错误,请告诉我。

没有一种解决方案适合所有情况,但对于我的项目,我发现最简单的方法是扩展
IdentityUser
IdentityDbContext
类。下面是一段伪代码,它关注的是您需要更改/添加以使其正常工作的最小值

对于您的用户类:

public class DomainUser : IdentityUser
{
    public DomainUser(string userName) : base(userName) {}

    public DomainUser() {}
}
对于DbContext实现:

public class DomainModelContext : IdentityDbContext<DomainUser>
{
    public DomainModelContext()
        : base() {}

    public DomainModelContext(string nameOrConnectionString)
        : base(nameOrConnectionString) {}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}
公共类DomainModelContext:IdentityDbContext
{
公共域模型上下文()
:base(){}
公共域模型上下文(字符串名称或连接字符串)
:base(nameOrConnectionString){}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
基于模型创建(modelBuilder);
}
}
在Startup.Auth.cs中:

    public static Func<UserManager<DomainUser>> UserManagerFactory { get; set; }

    static Startup()
    {
        UserManagerFactory = () => new UserManager<DomainUser>(new UserStore<DomainUser>(new DomainModelContext()));
    }
public static Func UserManagerFactory{get;set;}
静态启动()
{
UserManagerFactory=()=>新的UserManager(新的UserStore(新的DomainModelContext());
}
另一个可能的选择是在DomainUser类和从IdentityUser继承的ApplicationUser类之间创建1-1关系。这将减少域模型和标识机制之间的耦合,特别是如果您使用WithRequiredDependent而不创建双向导航属性,例如:

modelBuilder.Entity<ApplicationUser>().HasRequired(au => au.DomainUser).WithRequiredPrincipal();
modelBuilder.Entity().HasRequired(au=>au.DomainUser).WithRequiredPrincipal();
modelBuilder.Entity<ApplicationUser>().HasRequired(au => au.DomainUser).WithRequiredPrincipal();