将 ASP.NET 身份与核心域模型解耦 - 洋葱架构
Posted
技术标签:
【中文标题】将 ASP.NET 身份与核心域模型解耦 - 洋葱架构【英文标题】:Decoupling ASP.NET Identity from the Core Domain Models - Onion Architecture 【发布时间】:2014-12-22 04:44:34 【问题描述】:我使用这个示例项目 (https://github.com/imranbaloch/ASPNETIdentityWithOnion) 作为我的应用程序架构,在这个示例中,核心完全从包括身份框架在内的基础设施中解构出来。
在这个示例中,作者使用了适配器模式来解耦核心身份类(IdentityUser、IdentityRole ...),并在核心层提供类似的类。
现在这个示例项目中的问题是域模型(产品、图像)没有与模仿身份的虚拟类(AppUser、ApplicationRole、AppliationUserRoles...)链接。
然后我修改了代码,添加了对 AppUser 的引用
public sealed class Image : BaseEntity
public Image()
Products = new HashSet<Product>();
public string Path get; set;
public AppUser AppUser get; set; // The Added Reference ...
public ICollection<Product> Products get; set;
如果我将“AppUser”导航属性放在“Image”类中,则创建的数据库将有四个新表,而不是身份框架的默认五个表。
我需要将这些表合并到默认表中。 怎么样?
编辑:
这是驻留在数据层中的身份模型(我无法从核心引用)。
public class ApplicationIdentityUser :
IdentityUser<int, ApplicationIdentityUserLogin, ApplicationIdentityUserRole, ApplicationIdentityUserClaim>, IDomainUser
public ApplicationIdentityUser()
: base()
Images = new HashSet<Image>();
public string Name get; set;
public virtual ICollection<Image> Images get; set;
public class ApplicationIdentityRole : IdentityRole<int, ApplicationIdentityUserRole>
public ApplicationIdentityRole()
public ApplicationIdentityRole(string name)Name = name;
public class ApplicationIdentityUserRole : IdentityUserRole<int>
public class ApplicationIdentityUserClaim : IdentityUserClaim<int>
public class ApplicationIdentityUserLogin : IdentityUserLogin<int>
这也是我在 OnModelCreating 方法中的模型构建器:
modelBuilder.Entity<Image>()
.Property(e => e.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Image>()
.HasMany(e => e.Products)
.WithRequired(e => e.Image)
.WillCascadeOnDelete(false);
modelBuilder.Entity<ApplicationIdentityUser>()
.Property(e => e.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ApplicationIdentityRole>()
.Property(e => e.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ApplicationIdentityUserClaim>()
.Property(e => e.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
【问题讨论】:
AspNet* 表是多余的。需要摆脱它们。检查您的 ApplicationDbContext 对这些表做了什么? 我的 ApplicationDbContext 实际上是从 IdentityDbContext 驱动的,我需要一种方法来存储用户信息,您的意思是我可以使用以 Application* 开头的表吗? 我是说应该只有一组 *Users、*UserClaims 等表。看起来您在 ApplicationDbContext 中进行继承的方式已被破坏,并且您没有覆盖Users
和 Roles
等,因此 EF 为您创建了两组表。检查一下。
好的,我会更新我的问题以包含实现细节
@trailmax 我已经更新了问题,请检查...
【参考方案1】:
好的,我已经通过执行以下操作解决了这个问题:
-
在核心中包含对 Microsoft.AspNet.Identity.Core 的依赖项
在AppUser上实现IUser接口(该接口来自
Microsoft.AspNet.Identity.Core)。
在ApplicationRole上实现IRole接口。
完全摆脱 IdentityDbContext 并仅从 DbContext 继承。
实现您自己的 IUserStore* 版本,提供您的 AppUser
实现您自己的 IRoleStore 版本,提供您的 ApplicationRole。
我知道依赖 Microsoft.AspNet.Identity.Core 听起来很奇怪,但我们只需要 IUser 接口,它基本上也被视为您的应用程序的核心域模型。
这里的终极想法是彻底摆脱 Microsoft.AspNet.Identity.EntityFramework。
有兴趣的开发者可以为此 +1,所以我可以在 GitHub 上上传完整的工作示例。
【讨论】:
我正要踏上这段旅程,因为我得出了相同的结论,即 Microsoft.AspNet.Identity.EntityFramework 是很好的示例代码,但如果您打算让用户成为一流的域,则会干扰您的域模型。 真的很想看看您是如何摆脱 IdentityDbContext 的,以及您是如何在 Image 实体中引用 ApplicationIdentityUser 的,因为它位于核心而不是数据层中!任何示例将不胜感激。 有机会在 GitHub 上放一个例子吗? 你要放样本吗? 我不这么认为,搜索存储库,你会发现 Microsoft.AspNet.Identity.EntityFramework【参考方案2】:我正在使用这个框架,不需要在每个实体中都有一个链接 为了获取 userID 参考,我在 BaseEntity 中添加了一个属性 UserIDBy ,这样每个实体都会继承它。
public abstract class BaseEntity
public int Id get; set;
public string UserIDBy get; set;
接下来,在 web 项目中,IdentityExtensions.cs
中已经有一个名为 GetUserId(this IIdentity identity)
的扩展方法,因此要在每个 Create 和 Edit 操作结果中存储 UserIDBy:
创建操作结果:
// POST: /Region/Create
[HttpPost]
public async Task<ActionResult> Create([Bind(Include = "RegionName")] Region region)
if (ModelState.IsValid)
// TODO: Add insert logic here
var id = User.Identity.GetUserId();
region.UserIDBy = id.ToString();
await _regionService.AddAsync(region);
return Json(new success = true );
return PartialView("_Create", region);
编辑操作结果:
//// POST: /Region/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "id,RegionName")] Region region)
if (ModelState.IsValid)
var id = User.Identity.GetUserId();
region.UserIDBy = id.ToString();
_regionService.Update(region);
return Json(new success = true );
return PartialView("_Edit", region);
别忘了导入它:
using Myapp.Web.Extensions;
【讨论】:
非常聪明。感谢分享。 ASPNETIdentityWithOnion 是一个很棒的 shell,但是创建与自定义表和身份框架的关系的麻烦并不值得。 你有实现 ASPNETIdentityWithOnion 的开源项目吗?【参考方案3】:偶然发现了这个,遇到了同样的问题。
标记答案的问题是它仍然引用 Microsoft.AspNet,这使得未来的 .NET Core 计划变得粗糙。
主要问题实际上是在内核中构建身份验证功能的一般尝试,这违背了目的。
考虑让用于 Web 身份验证和授权的内置功能保留在 Web 层中,并引用代表 Core 需求的 Core 用户对象(UserProfile?)。这也将简化切换到另一种身份验证方法 (AD)。
然后,根据您的偏好,您可以从 AspNetUser 引用 Core.UserProfile 以避免多次 SQL 调用,或者只是确保对 Core.UserProfile 操作具有良好的缓存策略。
这允许您控制与您的核心模型分开的身份验证方法。
【讨论】:
以上是关于将 ASP.NET 身份与核心域模型解耦 - 洋葱架构的主要内容,如果未能解决你的问题,请参考以下文章