将 ApplicationUser 和其他模型移出 MVC 项目
Moving ApplicationUser and other models out of MVC project
如何从默认的 ASP.Net Mvc / Identity 2.0 中拆分属性、功能和 classes?我正在与一些事情作斗争:
- 默认情况下,它想使用 OWIN 的上下文连接某种依赖注入,并控制管理器
- 它将 ApplicationDbContext 置于应用程序级别,我的架构要求它在 "lower" 级别可用。
- 它要求我在相同的 class 中声明任何属性作为作用于这些属性的功能(这不适合我的架构)
- ApplcationUser 模型依赖于 Asp.Net,如果我要将 POCO 移动到解决方案的非 MVC 层,我想打破它
应用架构:
我有一个包含多个层的解决方案:
- Api - 定义服务接口
- 域 - 存储表示业务域的 POCO 模型
- 业务 - 存储与域对象交互的逻辑,并使用服务
- 服务 - 服务的实现,包括Entity Framework,以及域对象的结构映射
- 应用程序 - 在本例中为 MVC 应用程序。
我的业务层只知道服务接口,不知道实现,我正在使用依赖注入来连接所有东西。
我有一些接口定义了数据服务的 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>();
}
}
如何从默认的 ASP.Net Mvc / Identity 2.0 中拆分属性、功能和 classes?我正在与一些事情作斗争:
- 默认情况下,它想使用 OWIN 的上下文连接某种依赖注入,并控制管理器
- 它将 ApplicationDbContext 置于应用程序级别,我的架构要求它在 "lower" 级别可用。
- 它要求我在相同的 class 中声明任何属性作为作用于这些属性的功能(这不适合我的架构)
- ApplcationUser 模型依赖于 Asp.Net,如果我要将 POCO 移动到解决方案的非 MVC 层,我想打破它
应用架构:
我有一个包含多个层的解决方案:
- Api - 定义服务接口
- 域 - 存储表示业务域的 POCO 模型
- 业务 - 存储与域对象交互的逻辑,并使用服务
- 服务 - 服务的实现,包括Entity Framework,以及域对象的结构映射
- 应用程序 - 在本例中为 MVC 应用程序。
我的业务层只知道服务接口,不知道实现,我正在使用依赖注入来连接所有东西。
我有一些接口定义了数据服务的 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>();
}
}