將 ApplicationUser 和其他模型移出 MVC 項目
如何從預設的 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.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>(); } }