集成ASP.NET身份进入现有的DbContext [英] Integrating ASP.NET Identity into Existing DbContext

查看:126
本文介绍了集成ASP.NET身份进入现有的DbContext的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个ASP.NET MVC 5项目在VS2013,.NET 4.5.1,使用实体框架6 code-第一。我有一个体面的大小数据库构建出来,有些工作(项目约两周)。我想现在的用户认证集成,但我不知道如何处理它。花费最多的一天经过研究,我决定给新的ASP.NET身份框架射过来不必编写自定义成员资格或角色提供商。我很困惑的是如何使其与现有的数据库/模式我把所有的工作。

目前我有员工称为对象保​​存基本员工信息(现在)。后经思考的问题了一整天,我决定从中分离鉴定成用户对象,这是什么身份想反正。话虽这么说,我怎么让这一切工作的?

下面是我的员工类:

 公共类员工:人{
    公众诠释雇员{搞定;组; }
    公共字节CompanyId {搞定;组; }
    公共字符串名称{
        获得{
            返回的String.Format({0} {1},this.FirstName,this.LastName);
        }
    }
    公共字符串密码{搞定;组; }
    公共布尔IsActive {搞定;组; }    公共虚拟的ICollection<地址>地址{搞定;组; }
    公共虚拟公司公司{搞定;组; }
    公共虚拟的ICollection<电子邮件和GT;电子邮件{搞定;组; }
    公共虚拟的ICollection<电话和GT;电话{搞定;组; }    公务员(){
        this.Addresses =新的List<地址>();
        this.Emails =新的List<电子邮件和GT;();
        this.Phones =新的List<电话及GT;();
    }
}

和我的的DbContext 派生类:

 公共类DatabaseContext:{的DbContext
    静态DatabaseContext(){
        Database.SetInitializer&所述; DatabaseContext>(新DatabaseInitializer());
    }    公共DatabaseContext()
        :基地(NAME = DatabaseContext){
        this.Database.Initialize(真);
    }    公共DatabaseContext(
        字符串的connectionString)
        :基地(的connectionString){
        this.Database.Initialize(真);
    }    /// DbSets ...    公共覆盖INT的SaveChanges(){
        尝试{
            返回base.SaveChanges();
        }赶上(DbEntityValidationException E){
            IEnumerable的<串GT;错误= e.EntityValidationErrors.SelectMany(
                X =>
                    x.ValidationErrors)。选择(
                X =>
                    的String.Format({0}:{1},x.PropertyName,x.ErrorMessage));            抛出新DbEntityValidationException(的string.join(;,错误),e.EntityValidationErrors);
        }
    }    保护覆盖无效OnModelCreating(
        DbModelBuilder模型构建器){
        modelBuilder.Ignore<协调>();        ///的Configs ...        base.OnModelCreating(模型构建器);
    }
}


解决方案

所以花费约一天左右读书和阅读后,我结束了建设自己的身份实现。首先我所做的就是把我现有的员工对象并扩展它从 IUSER&LT继承; INT> IUSER< INT> 是身份的2.0(目前处于alpha)的一部分,可以让主键的类型进行配置,以其他的东西比的接口字符串在1.0中默认值。因为这样我存储的数据,我的实现真的很具体。例如,员工可以有多个电子邮件对象涉及到它,并为我的应用程序,我想用电子邮件作为用户名。所以,我干脆将用户名属性返回员工的工作电子邮件:

 公共字符串用户名{
    获得{
        如果(this.WorkEmail!= NULL){
            返回this.WorkEmail.Address;
        }        返回null;
    }
    集合{
        ///这个属性是无法设定的。
    }
}

侧面说明,因为我不打算使用制定者的财产,有没有obsoleting它除了简单地离开它空的更清洁的方式?

继续,我还添加了 PasswordHash 属性。我说我自己的角色对象,从 IRole&LT继承; INT> 。最后的员工角色对象各有一个的ICollection< T> 链接到彼此。另一个方面说明,实体框架实现身份手动创建映射表的UserRole ,而不是利用它自己的配置能力,我似乎​​无法理解它背后的推理。在的UserRole 它创造并获取传递到 *商店的IT实现,但它并没有真正做什么特别的不是充当链接等。我在执行我只是用已经建立的链接,这当然是在数据库中创建一个映射表,但不暴露无谓到应用程序。我只是觉得好奇。

再继续,我的配置对象,我继续实施自己的 IUserStore IRoleStore 班创造性地称为 EmployeeStore Rolestore的

 公共类EmployeeStore:IQueryableUserStore<员工,INT>中IUserStore<员工,INT>中IUserPasswordStore<员工,INT>中IUserRoleStore<员工,INT>中IDisposable的{
    私人布尔处理完毕;
    私人IDatabaseRepository<的角色和GT; RolesRepository {搞定;组; }
    私人IDatabaseRepository<员工> EmployeesRepository {搞定;组; }    公共EmployeeStore(
        IDatabaseRepository<的角色和GT; rolesRepository,
        IDatabaseRepository<员工> employeesRepository){
        this.RolesRepository = rolesRepository;
        this.EmployeesRepository = employeesRepository;
    }    #区域IQueryableUserStore成员
    公众的IQueryable<员工>用户{
        获得{
            返回this.EmployeesRepository.Set;
        }
    }
    #endregion    #区域IUserStore成员
    公共异步任务CreateAsync(
        员工员工){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        等待this.EmployeesRepository.AddAndCommitAsync(员工);
    }    公共异步任务DeleteAsync(
        员工员工){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        等待this.EmployeesRepository.RemoveAndCommitAsync(员工);
    }    公共任务<员工> FindByIdAsync(
        INT雇员){
        this.ThrowIfDisposed();        返回Task.FromResult<员工>(this.EmployeesRepository.FindSingleOrDefault(
            U =>
                (u.Id ==雇员)));
    }    公共任务<员工> FindByNameAsync(
        字符串username){
        this.ThrowIfDisposed();        返回Task.FromResult<员工>(this.EmployeesRepository.FindSingleOrDefault(
            E =>
                (e.UserName ==用户名)));
    }    公共异步任务UpdateAsync(
        员工员工){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        等待this.EmployeesRepository.CommitAsync();
    }
    #endregion    #区域IDisposable的成员
    公共无效的Dispose(){
        this.Dispose(真);        GC.Sup pressFinalize(本);
    }    保护无效的Dispose(
        BOOL处置){
        this.Disposed = TRUE;
    }    私人无效ThrowIfDisposed(){
        如果(this.Disposed){
            抛出新的ObjectDisposedException(base.GetType()名称。);
        }
    }
    #endregion    #区域IUserPasswordStore成员
    公共任务<串GT; GetPasswordHashAsync(
        员工员工){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        返回Task.FromResult<串GT;(employee.PasswordHash);
    }    公共任务<布尔> HasPasswordAsync(
        员工员工){
        返回Task.FromResult<布尔>(String.IsNullOrEmpty(employee.PasswordHash)!);
    }    公共任务SetPasswordHashAsync(
        员工的员工,
        串passwordHash){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        employee.PasswordHash = passwordHash;        返回Task.FromResult&所述;诠释>(0);
    }
    #endregion    #区域IUserRoleStore成员
    公共任务AddToRoleAsync(
        员工的员工,
        字符串角色名){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        如果(String.IsNullOrEmpty(角色名)){
            抛出新的ArgumentNullException(角色名);
        }        角色角色= this.RolesRepository.FindSingleOrDefault(
            R =>
                (r.Name ==角色名));        如果(角色== NULL){
            抛出新的InvalidOperationException异常(未找到角色);
        }        employee.Roles.Add(作用);        返回Task.FromResult&所述;诠释>(0);
    }    公共任务<&IList的LT;串GT;> GetRolesAsync(
        员工员工){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        返回Task.FromResult<&IList的LT;串GT;>(employee.Roles.Select(
            R =>
                r.Name).ToList());
    }    公共任务<布尔> IsInRoleAsync(
        员工的员工,
        字符串角色名){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        如果(String.IsNullOrEmpty(角色名)){
            抛出新的ArgumentNullException(角色名);
        }        返回Task.FromResult<布尔>(employee.Roles.Any(
            R =>
                (r.Name ==角色名)));
    }    公共任务RemoveFromRoleAsync(
        员工的员工,
        字符串角色名){
        this.ThrowIfDisposed();        如果(员工== NULL){
            抛出新的ArgumentNullException(雇员);
        }        如果(String.IsNullOrEmpty(角色名)){
            抛出新的ArgumentNullException(角色名);
        }        角色角色= this.RolesRepository.FindSingleOrDefault(
            R =>
                (r.Name ==角色名));        如果(角色== NULL){
            抛出新的InvalidOperationException异常(角色为空);
        }        employee.Roles.Remove(作用);        返回Task.FromResult&所述;诠释>(0);
    }
    #endregion
}

Rolestore的

 公共类Rolestore的:IQueryableRoleStore<作用,INT>中IRoleStore<作用,INT>中IDisposable的{
    私人布尔处理完毕;
    私人IDatabaseRepository<的角色和GT; RolesRepository {搞定;组; }    公共Rolestore的(
        IDatabaseRepository<的角色和GT; rolesRepository){
        this.RolesRepository = rolesRepository;
    }    #区域IQueryableRoleStore成员
    公众的IQueryable<的角色和GT;角色{
        获得{
            返回this.RolesRepository.Set;
        }
    }
    #endregion    #区域IRoleStore成员
    公共异步任务CreateAsync(
        角色角色){
        this.ThrowIfDisposed();        如果(角色== NULL){
            抛出新的ArgumentNullException(角色);
        }        等待this.RolesRepository.AddAndCommitAsync(作用);
    }    公共异步任务DeleteAsync(
        角色角色){
        this.ThrowIfDisposed();        如果(角色== NULL){
            抛出新的ArgumentNullException(角色);
        }        等待this.RolesRepository.RemoveAndCommitAsync(作用);
    }    公共任务<的角色和GT; FindByIdAsync(
        INT角色ID){
        this.ThrowIfDisposed();        返回Task.FromResult<的角色和GT;(this.RolesRepository.FindSingleOrDefault(
            R =>
                (r.Id ==角色ID)));
    }    公共任务<的角色和GT; FindByNameAsync(
        字符串角色名){
        this.ThrowIfDisposed();        返回Task.FromResult<的角色和GT;(this.RolesRepository.FindSingleOrDefault(
            R =>
                (r.Name ==角色名)));
    }    公共异步任务UpdateAsync(
        角色角色){
        this.ThrowIfDisposed();        如果(角色== NULL){
            抛出新的ArgumentNullException(角色);
        }        等待this.RolesRepository.CommitAsync();
    }
    #endregion    #区域IDisposable的成员
    公共无效的Dispose(){
        this.Dispose(真);        GC.Sup pressFinalize(本);
    }    保护无效的Dispose(
        BOOL处置){
        this.Disposed = TRUE;
    }    私人无效ThrowIfDisposed(){
        如果(this.Disposed){
            抛出新的ObjectDisposedException(base.GetType()名称。);
        }
    }
    #endregion
}

现在,我注意到的是,实体框架的实施是创造什么看起来就像一个小型的存储库。因为我的项目已经用我自己的仓库实现,我决定利用它来代替。我们将看到如何继续下去...

现在,这一切的作品,令人惊讶的不会崩溃所有,或至少目前还没有。话虽这么说,我有所有这些奇妙的身份实现,但我似乎无法弄清楚如何利用他们我的MVC应用程序中。因为这超出范围的这个问题,我会继续前进,打开一个新的寻址。

我要离开这个作为答案,以防有人的问题别人运行到这个在未来。当然,如果有人看到一个错误在code我已经张贴,请让我知道。

I'm working on an ASP.NET MVC 5 project in VS2013, .NET 4.5.1, that uses Entity Framework 6 Code-First. I have a decent size database built out and somewhat working (project is about two weeks old). I want to integrate user authentication now, but I'm not sure how to approach it. After spending most of the day researching I've decided to give the new ASP.NET Identity framework a shot over having to write custom Membership or Role providers. What I'm confused about is how to make it all work with the existing database/model I have.

Currently I have an object called Employee that holds basic employee information (for now). After having pondered the question all day, I decided to decouple authentication from it into a User object, which is what Identity wants anyway. That being said how do I make it all work?

Here's my Employee class:

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

And my DbContext derived class:

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

解决方案

So after spending about a day or so reading and reading, I ended up building my own Identity implementation. First what I did was take my existing Employee object and extended it to inherit from IUser<int>. IUser<int> is an interface that's a part of Identity 2.0 (currently in alpha) that allows the primary key type to be configured to something other than string as was default in 1.0. Because of the way I'm storing data, my implementation was really specific. For example, an Employee can have multiple Email objects related to it, and for my application I wanted to use emails as the user names. So, I simply set the UserName property to return the Employee's work email:

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

        return null;
    }
    set {
        /// This property is non-settable.
    }
}

Side note, since I'm not going to be using the setter for the property, is there a cleaner way of obsoleting it other than simply leaving it empty?

Moving on, I also added the PasswordHash property. I added my own Role object, inheriting from IRole<int>. Lastly the Employee and Role objects each have an ICollection<T> linking to each other. Another side note, the Entity Framework implementation of Identity manually creates the mapping table UserRoles rather than leveraging it's own configuration capabilities and I can't seem to understand the reasoning behind it. The UserRole it creates does get passed into the *Stores it implements, but it doesn't really do anything special other than act as a link. In my implementation I simply used the already established link, which of course creates a mapping table in the database, but is not pointlessly exposed into the application. I just find it curious.

Moving on again, with my configured objects I went ahead and implemented my own IUserStore and IRoleStore classes creatively called EmployeeStore and 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
}

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
}

Now, what I noticed was that the Entity Framework implementation was creating what looked like a mini-repository. Since my project was already using my own Repository implementation, I decided to leverage it instead. We'll see how that goes...

Now, all of this works and surprisingly does not crash at all, or at least hasn't yet. That being said, I have all of these wonderful Identity implementations, yet I can't seem to figure out how to leverage them inside my MVC application. Since that falls out of scope for this question, I'll go ahead and open a new one addressing that.

I'm leaving this as the answer to the question in case someone else runs into this in the future. Of course, if anyone sees an error in the code I've posted, please let me know.

这篇关于集成ASP.NET身份进入现有的DbContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆