从另一个项目继承(?)IdentityUser

Inherit (?) IdentityUser from another project

我的解决方案中有多个项目,都是 .NET Core 3.1。其中之一是我的核心项目(“A Project”),其中我只有基本模型 classes,没有方法或数据库访问权限。出于演示目的,下面是我的 Address.csUser.cs 文件的简化版本:

public class Address 
{
     public int Id {get;set;}
     public string AddressText {get;set;}
     public virtual User User {get;set;}
}

public class User
{
    public int UserId {get;set;}
    public int UserName {get;set;}

    public ICollection<Address> {get;set;}
}

在另一个项目(“B 项目”)中,我将构建实际功能。这个项目已经有 ASP.NET Core Identity 设置和 ApplicationUser class,它派生自 IdentityUser 并添加了一些自定义属性。

这是我 运行 遇到问题的地方。 Address.User 属性 必须设置为 ApplicationUser 的实例,但 ApplicationUser 位于 B 项目 .

显然,我没有 ASP.NET Core Identity 在我的 A Project 中设置为依赖项,所以我无法移动 ApplicationUser class进入那个项目。此外,我不能将 ApplicationUser 分配给 Address.User 属性 因为 ApplicationUser 不是从 User.

派生的

经过一些研究,我发现了一些不同的建议。一个建议是为 ASP.NET 核心身份组件使用一个单独的项目,然后在我的 B 项目 中将其与我的 A 项目 一起引用].另一个灵魂建议创建我自己的 UserStore

我不希望我的 A 项目 依赖于任何东西。但是我没有足够的经验来决定哪个选项更适合我的场景。

我以前肯定把自己画在这个特定的角落里!

您可以采取一些策略来解决这个问题,包括您列出的两个。然而,我推荐的方法是使用 interfaces.

总结

您将拥有 IUser 界面 [=] 而不是具体的 User class 83=] 您将从 项目 A 中的模型中引用它。然后,您将 IUser 界面应用到您的 ApplicationUser class。这将允许 ApplicationUser 的实例分配给您的,例如Address.User 属性,尽管 Address 并不知道 ApplicationUser

例子

项目 A 中,您将 classes 更新为如下所示:

public class Address 
{
     public int Id {get;set;}
     public string AddressText {get;set;}
     public virtual IUser User {get;set;}
}

public interface IUser
{
    int UserId {get;set;}
    int UserName {get;set;}
    ICollection<Address> {get;set;}
}

然后,在 项目 B 中,您将 IUser 接口应用到您的 ApplicationUser class 并确保它实现了所需的属性:

public class ApplicationUser: IdentityUser, IUser 
{
  … 
  public int UserId {get;set;}
  public ICollection<Address> {get;set;}
}

Note: You don’t need to implement UserName, as that’s already been implemented on IdentityUser. Though, of course, you can always override the property, should the need arise (e.g., to add validation attributes).

限制

当您访问例如您的 Address.User 属性 时,您将只能访问您在 IUser 上定义的成员。如果您需要访问在 ApplicationUserIdentityUser 上定义的任何其他成员,您首先需要将 IUser 引用转换为 ApplicationUser;例如,

var user = address.User as ApplicationUser;
var emailConfirmed = user?.EmailConfirmed?? false;

当然,如果您知道您将需要访问这些成员,您只需确保它们已在您的界面上定义,而不必担心关于这个。

注意事项

有几个注意事项值得注意。这些可能不适用于您,但为了完整起见,我想将它们包括在内。

O/RM

正如我在评论中提到的,如果您使用 O/RM 来填充您的模型(例如 Entity Framework (EF) Core),您可能 运行 会遇到识别问题的问题在单独的程序集中具体实现您的接口。这是可以做到的,但它肯定会增加您可能不想与之抗衡的复杂性!但是,如果您手动构建对象图,这将不是问题。

身份与用户模型

IdentityUser 代表当前已验证用户,而不是一般用户参考。例如,在电子商务应用程序中,构造一个 IdentityUser 来引用每个产品的卖家是没有意义的。这里显然存在重叠,使用一个数据源来提供两者就可以了。但也有一些属性(例如 PasswordHashSecurityStamp)在一般用户模型上填充没有意义。您最终可能会发现这些需求相互冲突。


在上述任一情况下,您可能会发现更容易区分 ApplicationUserUser class。这不是您要求的,但值得考虑。在那种情况下,@RomanKalinchuk 的方法更有意义。尽管如此,即便如此,您仍然可以通过对每个应用相同的 IUser 接口来统一它们,从而确保它们共享一组核心属性。

这只是关于如何解决这个特殊情况的意见

您有一些域 class,例如用户、地址等 你所有的应用程序域属性都在那里 像年龄、性别、名字、姓氏、电子邮件、ID、城市、街道,这些由您的应用程序用户填充,可以在应用程序个人区域查看,例如用于用于交付、通知等

并且您有授权部分。 IdentityUser class (AspNetUsers table) 包含有关用户凭据(用户名、散列密码等)的数据。此数据的唯一用途是检查当前可用的操作

某些操作或控制器可以用 Authorize 属性标记,并且需要用户在您的系统中进行身份验证。

所以域 User 和授权 IdentityUser classes 可以完全独立存在,这将满足您的需求

但是如何获取当前域用户呢?

你可以这样做

public interface IUserService
{
    User GetUserByEmail(string email);
}

[Authorize]
public class UserController : Controller
{
    private readonly IUserService _userService;
    public UserController(IUserService userService)
    {
        _userService = userService;
    }

    public User GetCurrentUser()
    {
        var userName = HttpContext.User.Identity.Name;
        return _userService.GetUserByEmail(userName);
    }
}