将ASP.NET身份与核心域模型分离 - 洋葱架构

时间:2014-10-27 02:12:59

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

我正在使用这个示例项目(https://github.com/imranbaloch/ASPNETIdentityWithOnion)作为我的应用程序架构,在此示例中,核心从包括身份框架在内的基础设施中完全取消。

在此示例中,作者使用适配器模式来分离核心标识类(IdentityUser,IdentityRole ...),并在Core层中提供类似它们的类。

现在这个示例项目中的问题是域模型(产品,图像)没有与模仿身份类的虚拟类(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; }
}

如果我把&#34; AppUser&#34;导航属性&#34;图像&#34;在类中,创建的数据库将具有除标识框架的默认FIVE表之外的四个新表。

Onion Database Problem with Identity Tables

我需要将这些表合并为默认表。 怎么样?

修改

这是驻留在数据层中的身份模型(我无法从核心引用)。

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);

3 个答案:

答案 0 :(得分:20)

好的,我已经通过以下方式解决了这个问题:

  1. 在您的Core中包含Microsoft.AspNet.Identity.Core
  2. 的依赖项
  3. 在AppUser上实现 IUser 界面(此界面来自 Microsoft.AspNet.Identity.Core)。
  4. ApplicationRole 上实施 IRole 界面。
  5. 完全摆脱 IdentityDbContext 并仅从 DbContext 继承。
  6. 实施您自己的 IUserStore * 版本,提供 AppUser
  7. 实施您自己的 IRoleStore 版本,提供 ApplicationRole
  8. 我知道依赖于Microsoft.AspNet.Identity.Core听起来很奇怪,但是我们只需要接口IUser,它基本上也被认为是你的应用程序的核心域模型。

    这里的终极理念是完全获取Microsoft.AspNet.Identity.EntityFramework

    感兴趣的开发者可以+1这个,所以我可以在GitHub上传一个完整的工作样本。

答案 1 :(得分:1)

我正在使用这个框架,不需要在每个实体中都有链接 为了获取userID引用,我在BaseEntity中添加了一个属性UserIDBy,因此每个实体都将继承它。

public abstract class BaseEntity
{
    public int Id { get; set; }
    public string UserIDBy { get; set; }
}

接下来,在Web项目中,GetUserId(this IIdentity identity)中已经有一个名为IdentityExtensions.cs的扩展方法,因此要在每个创建和编辑操作结果中存储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;

答案 2 :(得分:0)

偶然发现了这个,遇到同样的问题。

明确答案的问题在于它仍然引用了Microsoft.AspNet,这使得它对未来的.NET核心计划变得粗略。

主要问题实际上是在核心中构建身份验证功能的一般尝试,这会破坏目的。

考虑让Web身份验证和授权的内置功能保留在Web层中,并引用代表Core需求的Core用户对象(UserProfile?)。这还可以简化切换到另一种身份验证方法(AD)。

根据您的偏好,您可以从AspNetUser引用Core.UserProfile以避免多次SQL调用,或者只是确保在Core.UserProfile操作上有一个良好的缓存策略。

这使您可以将您的身份验证方法与Core模型分开控制。