Asp.net-Mvc

將 ApplicationUser 和其他模型移出 MVC 項目

  • July 30, 2016

如何從預設的 ASP.Net Mvc / Identity 2.0 中拆分屬性、功能和類?我正在與幾件事作鬥爭:

  • 預設情況下,它想使用 OWIN 的上下文來連接某種依賴注入,並控制管理器
  • 它將 ApplicationDbContext 放在應用程序級別,我的架構要求它在“較低”級別可用。
  • 它要求我在與作用於這些屬性的功能相同的類中聲明任何屬性(這不適合我的架構)
  • ApplcationUser 模型依賴於 Asp.Net,如果我要將 POCO 移動到解決方案的非 MVC 層,我想打破它

應用架構:

我有一個有幾個層次的解決方案:

  • Api - 為服務定義介面
  • 域 - 儲存代表業務域的 POCO 模型
  • 業務 - 儲存與域對象互動的邏輯,並使用服務
  • 服務 - 服務的實現,包括實體框架和域對象的結構映射
  • 應用程序 - 在這種情況下,一個 MVC 應用程序。

我的業務層只知道服務介面,不知道實現,我使用依賴注入來連接所有東西。

我有一些定義數據服務的讀/寫/工作單元操作的介面,以及從 DbContext (在我的服務層中)繼承的這些介面的實現。我沒有一系列的DbSet<MyPoco> MyPocos {get;set;},而是通過傳遞一系列定義關係的類型配置來連接它,然後通過Set<Type>(). 所有這些都很好。

此堆棧已用於現有應用程序,並且執行良好。我知道它將過渡到 MVC 應用程序,並且只有“開箱即用”的 ASP.Net Identity-2 存在問題。

我對此的解決方案是:抽象所有的東西

我通過將身份的大部分功能抽像到它自己的項目中來解決這個問題,這使得在其他項目中更容易進行單元測試和重用抽象。

看完這篇文章我有了想法

具有模式的持久性無知 ASP.NET 身份

然後我微調了這個想法以滿足我的需要。我基本上只是將我需要的一切從 asp.net.identity 換成了我的自定義介面,這些介面或多或少反映了框架提供的功能,但具有更容易抽象而不是實現的優勢。

身份使用者

/// <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
}

身份管理器

/// <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
}

身份結果

/// <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 模型
  • 業務 - 儲存與域對象互動的邏輯,並使用服務
  • 服務 - 服務的實現,包括實體框架和域對象的結構映射
  • 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>();
   }
}

引用自:https://stackoverflow.com/questions/38669833