>默认情况下,它希望使用OWIN的上下文来连接某种依赖注入,并控制Managers
>它将ApplicationDbContext放在应用程序级别,我的架构要求它在“较低”级别可用.
>它要求我声明与作用于这些属性的功能相同的类中的任何属性(不适合我的体系结构)
> ApplcationUser模型依赖于Asp.Net,如果我要将POCO移动到解决方案的非MVC层,我想打破它
应用架构:
我有一个有几层的解决方案:
> Api – 定义服务接口
>域 – 存储代表业务域的POCO模型
>业务 – 存储用于与域对象交互并消耗服务的逻辑
>服务 – 服务的实现,包括实体框架,以及域对象的结构图
>应用程序 – 在这种情况下,是一个MVC应用程序.
我的业务层只知道服务接口,而不是实现,我使用依赖注入来连接所有东西.
我有一些接口定义数据服务的读/写/工作单元操作,以及从DbContext(在我的服务层)继承的这些接口的实现.而不是有一系列的DbSet< MyPoco> MyPocos {get; set;},我通过传递一系列定义关系的类型配置,然后通过Set< Type>()访问我的类型来连接它.一切都很好.
此堆栈已用于现有应用程序,并且运行良好.我知道它将转换到MVC应用程序,并且只有“开箱即用”ASP.Net Identity-2的问题.
解决方法
我通过将身份的大部分功能抽象到自己的项目中来解决这个问题,这样可以更容易地进行单元测试并在其他项目中重用抽象.
读完这篇文章后我明白了
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 }
应用程序层只知道抽象,并且在启动时配置实现.我没有在更高级别使用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>(); } }