将 ApplicationUser 和其他模型移出 MVC 项目

Moving ApplicationUser and other models out of MVC project

如何从默认的 ASP.Net Mvc / Identity 2.0 中拆分属性、功能和 classes?我正在与一些事情作斗争:

应用架构:

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

我的业务层只知道服务接口,不知道实现,我正在使用依赖注入来连接所有东西。

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

此堆栈已用于现有应用程序,并且运行良好。我知道它将过渡到 MVC 应用程序,并且我只有 "out of the box" ASP.Net Identity-2.

有问题

我的解决方案是:抽象所有东西

我通过将 identity 的大部分功能抽象到它自己的项目中来解决这个问题,这样可以更轻松地进行单元测试和在其他项目中重用抽象。

看完这篇文章我明白了

Persistence-Ignorant ASP.NET Identity with Patterns

然后我微调了这个想法以满足我的需要。我基本上只是将我需要的所有东西从 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 模型
  • 业务 - 存储与域对象交互的逻辑,并使用服务
  • 服务 - 服务的实现,包括Entity Framework,以及域对象的结构映射
  • Identity - 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>();
    }
}