【问题标题】:Decoupling ASP.NET Identity from the Core Domain Models - Onion Architecture将 ASP.NET 身份与核心域模型解耦 - 洋葱架构
【发布时间】: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 中进行继承的方式已被破坏,并且您没有覆盖 UsersRoles 等,因此 EF 为您创建了两组表。检查一下。
  • 好的,我会更新我的问题以包含实现细节
  • @trailmax 我已经更新了问题,请检查...

标签: .net entity-framework-6 asp.net-identity onion-architecture


【解决方案1】:

好的,我已经通过执行以下操作解决了这个问题:

  1. 在核心中包含对 Microsoft.AspNet.Identity.Core 的依赖项
  2. 在AppUser上实现IUser接口(该接口来自 Microsoft.AspNet.Identity.Core)。
  3. ApplicationRole上实现IRole接口。
  4. 完全摆脱 IdentityDbContext 并仅从 DbContext 继承。
  5. 实现您自己的 IUserStore* 版本,提供您的 AppUser
  6. 实现您自己的 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 操作具有良好的缓存策略。

这允许您控制与您的核心模型分开的身份验证方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-09-24
    • 2014-10-15
    • 2020-08-13
    • 2014-06-22
    • 2015-03-17
    • 2011-10-09
    • 1970-01-01
    • 2015-11-06
    相关资源
    最近更新 更多