将ASP.NET身份集成到现有的DBContext中



我正在使用VS2013,.NET 4.5.1中的ASP.NET MVC 5项目,该项目使用实体框架6代码 - 第一。我有一个不错的尺寸数据库,并且有些工作(项目大约两个星期大)。我想立即整合用户身份验证,但是我不确定如何处理。在整天研究大部分时间之后,我决定将新的ASP.NET身份框架提供一个镜头,因为必须编写自定义会员或角色提供者。我感到困惑的是如何使其与现有数据库/模型一起使用。

目前,我有一个称为Employee的对象,该对象掌握了基本的员工信息(目前)。在整天思考问题之后,我决定将身份验证从中验证到User对象,这是身份想要的。话虽这么说,我该如何使一切都起作用?

这是我的Employee类:

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>();
    }
}

和我的DbContext派生类:

public class DatabaseContext : DbContext {
    static DatabaseContext() {
        Database.SetInitializer<DatabaseContext>(new DatabaseInitializer());
    }
    public DatabaseContext()
        : base("Name=DatabaseContext") {
        this.Database.Initialize(true);
    }
    public DatabaseContext(
        string connectionString)
        : base(connectionString) {
        this.Database.Initialize(true);
    }
    /// DbSets...
    public override int SaveChanges() {
        try {
            return base.SaveChanges();
        } catch (DbEntityValidationException e) {
            IEnumerable<string> errors = e.EntityValidationErrors.SelectMany(
                x =>
                    x.ValidationErrors).Select(
                x =>
                    String.Format("{0}: {1}", x.PropertyName, x.ErrorMessage));
            throw new DbEntityValidationException(String.Join("; ", errors), e.EntityValidationErrors);
        }
    }
    protected override void OnModelCreating(
        DbModelBuilder modelBuilder) {
        modelBuilder.Ignore<Coordinate>();
        /// Configs...
        base.OnModelCreating(modelBuilder);
    }
}

因此,在花费大约一天左右的阅读和阅读之后,我最终建立了自己的身份实现。首先,我要做的是将现有的Employee对象扩展到从IUser<int>继承。IUser<int>是一个接口,是Identity 2.0(当前在Alpha中)的一部分,它允许将主密钥类型配置为string以外的其他功能,如1.0中的默认值。由于我存储数据的方式,我的实现确实是具体的。例如,Employee可以具有与之相关的多个Email对象,对于我的应用程序,我想将电子邮件用作用户名。因此,我只是将UserName属性设置为返回Employee的工作电子邮件:

public string UserName {
    get {
        if (this.WorkEmail != null) {
            return this.WorkEmail.Address;
        }
        return null;
    }
    set {
        /// This property is non-settable.
    }
}

旁注,既然我不会使用六个式设置器作为属性,那么除了简单地将其留空之外,是否有一种更干净的方法?

继续前进,我还添加了PasswordHash属性。我添加了自己的Role对象,从IRole<int>继承。最后,EmployeeRole对象每个都有一个ICollection<T>彼此链接。另一个旁注是,身份的实体框架实现手动创建映射表UserRoles,而不是利用其自己的配置功能,我似乎无法理解其背后的原因。IT创建的UserRole确实传递到了IT实施的*Store s中,但是除了充当链接外,它并没有做任何其他事情。在我的实施中,我只是使用已经建立的链接,该链接当然会在数据库中创建一个映射表,但并没有毫无意义地暴露在应用程序中。我只是觉得很好奇。

再次继续前进,使用我的配置对象,我继续实现自己的IUserStoreIRoleStore类,称为EmployeeStoreRoleStore

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
}

RoleStore

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应用程序中利用它们。由于这个问题的范围不足,我将继续开设一个新的解决方案。

如果其他人将来会遇到这个问题,我将其作为问题的答案。当然,如果有人在我发布的代码中看到错误,请让我知道。

看一下SimpleSecurity项目源代码代码ASP.NET身份的数据库上下文如何扩展到包括新表的一个示例。这可能适合您的情况。这是通过从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());
    }
}

SimpleSecurity项目从您的MVC应用程序中脱离ASP.NET身份并扩展它。

由于您的员工类似乎是会员资格的用户配置文件,因此我将考虑将其定制,以适合您在ASP.NET Identity中自定义用户配置文件的方式,此处将在此处讨论。基本上,您的员工类需要从IdentityUser继承,您将从员工中删除密码属性,因为这是在IdentityUser中定义的,并且该框架在此处寻找它。然后,当定义上下文时,您将使用员工类,以便看起来像这样的东西

public class DatabaseContext : IdentityDbContext<Employee>
{
  ...
}

没有一个解决方案适合所有情况,但是对于我的项目,我发现最简单的事情是扩展IdentityUserIdentityDbContext类。以下是伪代码,重点是您需要更改/添加以使此工作的最低限度。

对于您的用户类:

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);
    }
}

和在startup.auth.cs中:

    public static Func<UserManager<DomainUser>> UserManagerFactory { get; set; }
    static Startup()
    {
        UserManagerFactory = () => new UserManager<DomainUser>(new UserStore<DomainUser>(new DomainModelContext()));
    }

另一个潜在的选项是在域使用类别和从IdentityUser继承的应用程序类别之间创建1-1关系。这将减少您的域模型和身份机制之间的耦合,尤其是如果您不需要创建双向导航属性,则依赖于依赖型号,这是这样的:

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

相关内容

  • 没有找到相关文章

最新更新