将ApplicationUser和其他模型移出MVC项目 [英] Moving ApplicationUser and other models out of MVC project

查看:69
本文介绍了将ApplicationUser和其他模型移出MVC项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何从默认的ASP.Net Mvc/Identity 2.0中拆分属性,功能和类?我在与几件事作斗争:

  • 默认情况下,它希望使用OWIN的上下文来进行某种依赖注入,并控制管理器.
  • 它将ApplicationDbContext置于应用程序级别,而我的体系结构要求它在较低"级别可用.
  • 它要求我在与作用在这些属性上的功能相同的类中声明所有属性(这与我的体系结构不匹配)
  • ApplcationUser模型依赖于Asp.Net,如果要将POCO移至解决方案的非MVC层,我想打破它.

应用架构:

我有一个包含多个层次的解决方案:

  • Api-定义服务接口
  • 域-存储代表业务域的POCO模型
  • 业务-存储用于与域对象进行交互的逻辑,并使用服务
  • 服务-服务的实现,包括实体框架和域对象的结构映射
  • 应用程序-在这种情况下,是MVC应用程序.

我的业务层只知道服务接口,而不是实现,并且我正在使用依赖注入来连接一切.

我有一些接口定义数据服务的读/写/工作单元操作,以及从DbContext继承的这些实现(在我的服务层中).通过传递定义定义关系的一系列类型配置,然后通过Set<Type>()访问我的类型,而不是使用一系列的DbSet<MyPoco> MyPocos {get;set;}进行连接.一切都很好.

该堆栈已存在于现有应用程序中,并且运行良好.我知道它将过渡到MVC应用程序,并且仅在开箱即用"的ASP.Net Identity-2中遇到问题.

解决方案

我对此的解决方案是:抽象所有内容

我通过将身份的大多数功能抽象到其自己的项目中来解决此问题,这使单元测试更加容易,并且可以在其他项目中重复使用抽象.

阅读这篇文章后我有了主意

具有模式的持久性无关的ASP.NET身份

然后我微调了这个想法以适应我的需求.基本上,我只是从asp.net.identity中交换了我所需的所有内容,用于我的自定义接口,该接口或多或少地反映了框架提供的功能,但具有更容易抽象而不是实现的优点.

IIdentityUser

/// <summary>
///  Minimal interface for a user with an id of type <seealso cref="System.String"/>
/// </summary>
public interface IIdentityUser : IIdentityUser<string> { }
/// <summary>
///  Minimal interface for a user
/// </summary>
public interface IIdentityUser<TKey>
    where TKey : System.IEquatable<TKey> {
    TKey Id { get; set; }
    string UserName { get; set; }
    string Email { get; set; }
    //...other code removed for brevity
}

IIdentityManager

/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager : IIdentityManager<IIdentityUser> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser> : IIdentityManager<TUser, string>
    where TUser : class, IIdentityUser<string> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser, TKey> : IDisposable
    where TUser : class, IIdentityUser<TKey>
    where TKey : System.IEquatable<TKey> {
    //...other code removed for brevity
}

IIdentityResult

/// <summary>
/// Represents the minimal result of an identity operation
/// </summary>
public interface IIdentityResult : System.Collections.Generic.IEnumerable<string> {
    bool Succeeded { get; }
}

在身份管理器的默认实现中,该管理器也存在于其自己的项目中,我只包装了ApplicationManager,然后在类型与asp.net.identity类型之间映射了结果和功能.

public class DefaultUserManager : IIdentityManager {
    private ApplicationUserManager innerManager;

    public DefaultUserManager() {
        this.innerManager = ApplicationUserManager.Instance;
    }
    //..other code removed for brevity
    public async Task<IIdentityResult> ConfirmEmailAsync(string userId, string token) {
        var result = await innerManager.ConfirmEmailAsync(userId, token);
        return result.AsIIdentityResult();
    }
    //...other code removed for brevity
}

应用程序层仅了解抽象,并且在启动时配置了实现.我没有更高级别的using Microsoft.AspNet.Identity,因为它们都使用局部抽象.

这些层可能看起来像这样:

  • Api-定义服务的接口(包括身份抽象接口)
  • 域-存储代表业务域的POCO模型
  • 业务-存储用于与域对象进行交互的逻辑,并使用服务
  • 服务-服务的实现,包括实体框架和域对象的结构映射
  • 身份-Microsoft.AspNet.Identity特定服务的实现,包括Microsoft.AspNet.Identity.EntityFramework;和OWIN配置
  • 应用程序-在这种情况下,是MVC应用程序.

在MVC应用程序层中,因此只需AccountController

using MyNamespace.Identity.Abstractions

public partial class AccountController : Controller {
    private readonly IIdentityManager userManager;

    public AccountController(IIdentityManager userManager) {
        this.userManager = userManager;
    }

    //...other code removed for brevity

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Signin(LoginViewModel model, string returnUrl) {
        if (ModelState.IsValid) {
            // authenticate user
            var user = await userManager.FindAsync(model.UserName, model.Password);
            if (user != null) {
                //...code removed for brevity
            } else {
                // login failed
                setFailedLoginIncrementalDelay();
                ModelState.AddModelError("", "Invalid user name or password provided.");
            }
        }
        //TODO: Audit failed login

        // If we got this far, something failed, redisplay form
        return View(model);  
    }
}

这假设您正在使用某些DI框架.只有在IoC的配置中,才会提及实现身份的层,从而将身份完全从需要使用身份的层中抽象出来.

//NOTE: This is custom code.
protected override void ConfigureDependencies(IContainerBuilder builder) {
    if (!builder.HasHandler(typeof(IIdentityManager))) {
        builder.PerRequest<IIdentityManager, DefaultUserManager>();
    }
}

How can I split the properties, functionality and classes out of the default ASP.Net Mvc / Identity 2.0? I am battling with a few things:

  • by default, it wants to use OWIN's context to wire up some kind of dependency injection, and control the Managers
  • it puts the ApplicationDbContext at the application level, where my architecture requires it be available at "lower" levels.
  • It requires that I declare any properties in the same class as the functionality that acts on those properties (which doesn't fit in with my architecture)
  • The ApplcationUser model has dependencies on Asp.Net, which I would like to break if I am to move the POCO to a non-MVC layer of the solution

Application architecture:

I have a solution that has several tiers :

  • Api - defines interfaces for services
  • Domain - stores POCO models representing a business domain
  • Business - stores logic for interacting with domain objects, and consumes services
  • Service - implementation of services, including Entity Framework, and the structure maps for the domain objects
  • Application - in this case, an MVC application.

My business layer only knows about service interfaces, not implementation, and I am using dependency injection to wire everything up.

I have some interfaces defining read/write/unit of work operations for a data service, and an implementation of these that inherit from DbContext (in my Service layer). Instead of having a series of DbSet<MyPoco> MyPocos {get;set;}, I am wiring it up by passing a series of type configurations that define relationships, then accessing my types through Set<Type>(). All that works great.

This stack has been in place for an existing application, and works well. I know it will transition to an MVC application, and am only having issues with the "out of the box" ASP.Net Identity-2.

解决方案

My solution to this was to: Abstract all the things

I got around this by abstracting most of the functionality of identity into its own project which allowed for easier unit testing and reuse of the abstraction in other projects.

I got the idea after reading this article

Persistence-Ignorant ASP.NET Identity with Patterns

I then fine tuned the idea to suit my needs. I basically just swapped out everything I needed from asp.net.identity for my custom interfaces which more or less mirrored the functionality provided by the framework but with the advantage of easier abstractions and not implementations.

IIdentityUser

/// <summary>
///  Minimal interface for a user with an id of type <seealso cref="System.String"/>
/// </summary>
public interface IIdentityUser : IIdentityUser<string> { }
/// <summary>
///  Minimal interface for a user
/// </summary>
public interface IIdentityUser<TKey>
    where TKey : System.IEquatable<TKey> {
    TKey Id { get; set; }
    string UserName { get; set; }
    string Email { get; set; }
    //...other code removed for brevity
}

IIdentityManager

/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager : IIdentityManager<IIdentityUser> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser> : IIdentityManager<TUser, string>
    where TUser : class, IIdentityUser<string> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser, TKey> : IDisposable
    where TUser : class, IIdentityUser<TKey>
    where TKey : System.IEquatable<TKey> {
    //...other code removed for brevity
}

IIdentityResult

/// <summary>
/// Represents the minimal result of an identity operation
/// </summary>
public interface IIdentityResult : System.Collections.Generic.IEnumerable<string> {
    bool Succeeded { get; }
}

In my default implementation of the identity manager, which also lives in its own project, I simply wrapped the ApplicationManager and then mapped results and functionality between my types and the asp.net.identity types.

public class DefaultUserManager : IIdentityManager {
    private ApplicationUserManager innerManager;

    public DefaultUserManager() {
        this.innerManager = ApplicationUserManager.Instance;
    }
    //..other code removed for brevity
    public async Task<IIdentityResult> ConfirmEmailAsync(string userId, string token) {
        var result = await innerManager.ConfirmEmailAsync(userId, token);
        return result.AsIIdentityResult();
    }
    //...other code removed for brevity
}

The application layer is only aware of the abstractions and the implementation is configured at startup. I don't have any using Microsoft.AspNet.Identity at the higher level as they are all using the local abstractions.

the tiers can look like this :

  • Api - defines interfaces for services (including Identity abstraction interfaces)
  • Domain - stores POCO models representing a business domain
  • Business - stores logic for interacting with domain objects, and consumes services
  • Service - implementation of services, including Entity Framework, and the structure maps for the domain objects
  • Identity - implementation of Microsoft.AspNet.Identity specific services, including Microsoft.AspNet.Identity.EntityFramework; and OWIN configuration
  • Application - in this case, an MVC application.

In MVC Application layer the AccountController thus only needed

using MyNamespace.Identity.Abstractions

public partial class AccountController : Controller {
    private readonly IIdentityManager userManager;

    public AccountController(IIdentityManager userManager) {
        this.userManager = userManager;
    }

    //...other code removed for brevity

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Signin(LoginViewModel model, string returnUrl) {
        if (ModelState.IsValid) {
            // authenticate user
            var user = await userManager.FindAsync(model.UserName, model.Password);
            if (user != null) {
                //...code removed for brevity
            } else {
                // login failed
                setFailedLoginIncrementalDelay();
                ModelState.AddModelError("", "Invalid user name or password provided.");
            }
        }
        //TODO: Audit failed login

        // If we got this far, something failed, redisplay form
        return View(model);  
    }
}

This assumes you are using some DI framework. It is only in the configuring of the IoC that any mention is made of the layer that implements identity completely abstracting it away from those that have need of using identity.

//NOTE: This is custom code.
protected override void ConfigureDependencies(IContainerBuilder builder) {
    if (!builder.HasHandler(typeof(IIdentityManager))) {
        builder.PerRequest<IIdentityManager, DefaultUserManager>();
    }
}

这篇关于将ApplicationUser和其他模型移出MVC项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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