【问题标题】:How do I use the UserManager from project A in project B?如何在项目 B 中使用项目 A 的 UserManager?
【发布时间】:2020-08-21 10:55:04
【问题描述】:

项目 A 和项目 B 都是 ASP NET Core 2.2 应用程序。

项目 B 将 Hangfire 用于后台作业,并且几乎没有做其他事情,而且它使用 Hangfire 的事实甚至可能并不重要(更多内容在底部)。项目 A 在 B 的 Hangfire 上排队工作。

现在,假设我的班级代表一个任务,称为 Job。这包含在项目 C 中,这是一个由项目 B 引用的普通旧类库,它又引用包含它正在使用的实体的其他项目。

依赖将通过构造函数注入到这个类中:

public class Job
{
    public Job(UserManager<ApplicationUser> userManager,
               IThisRepository thisRepository,
               IThatRepository thatRepository)
    {
    }

    public void Execute(string userId)
    {
        // this is where the work gets done
    }
}

并且在大多数情况下,它们确实被注入:IThisRepositoryIThatRepository 被注入并且它们工作......大部分时间。

在项目 B 的 Startup.cs(应该运行此作业的那个)中,我手动并成功注册了这些接口,以及它们需要其他一些东西的 DbContext

UserManager 手动注册相当困难,因为它的构造函数需要所有参数,所以由于我在工作中真的需要它,所以我决定进行一些更改。

现在,我正在使用的实体示例如下:

public class Category
{
    [Key]
    public int Id { get; set; }

    // several other properties of primitive types

    public ApplicationUser User { get; set; }

    [Required]
    public string UserId { get; set; }
}

public class Dish
{
    [Key]
    public int Id { get; set; }

    // several other properties of primitive types

    public ApplicationUser User { get; set; }

    [Required]
    public string UserId { get; set; }        

    public Category Category { get; set; }

    [Required]
    public string CategoryId { get; set; }
}

现在的问题是:在 Job 内部,我尝试创建一个新 Dish 并将其与用户和类别相关联。由于我只有用户 ID,而我无权访问 UserManager,这就是我尝试做的事情:

// ...

var category = await categoryRepository.FindByUserAndCode(userId, "ABC");

// this is a category that is guaranteed to exist

var dish = new Dish();
dish.UserId = userId;
// notice there's no dish.User assignment, because I don't have an ApplicationUser object
dish.Category = category;

dishRepository.Upsert(dish); (which internally either creates a new entity or updates the existing one as appropriate)

这就是一切都崩溃的地方,因为它说我试图插入的具有相同 ID 的类别已经存在,所以我试图复制一个主键。

由于该用户的代码为 ABC 的类别存在于数据库中,我认为这很奇怪。

事情是这样的:存储库返回的Category 实例确实填充了UserId 属性,但User 属性是null

我认为这是导致我的问题的原因:EF 可能看到该属性是 null 并认为该对象是一个新对象。

我不知道为什么会出现null(甚至对于其他所有具有引用用户的属性的实体也会出现),但我试图回溯,而不是只使用用户 ID,我想要尝试让 Hangfire 实例化 Job 并将 UserManager&lt;ApplicationUser&gt; 注入其中,因此至少我可以通过其 id 获取我的用户实例。

值得注意的是,这适用于项目 A 的其他部分,只是当我执行后台作业时出现了可怕的错误,我终生无法弄清楚它是什么。

但是UserManager 的依赖关系很多,我担心我可能会找错树或做错了。

我说我使用 Hangfire 的事实可能并不重要,因为它运行的假设是:只要给我你的类的名称,我会负责实例化它只要 em> 所有的依赖都注册好了。

有没有人做过这件事,可以帮忙解释一下吗?

【问题讨论】:

    标签: c# entity-framework-core asp.net-core-2.2 hangfire


    【解决方案1】:

    您在这里包含了绝对大量的信息,这些信息完全与手头的问题无关。您的问题归结为只是尝试添加菜肴时遇到的异常:“我尝试插入的具有相同 ID 的类别已经存在,因此我正在尝试复制主键。”

    这通常是由于尝试使用分离的实体作为关系引起的,即:

    dish.Category = category;
    

    如果 category 与上下文分离,则 EF 将因为此分配尝试创建它,并且由于它已经存在,因此创建失败。我们看不到categoryRepository.FindByUserAndCode 中发生了什么,但我想您要么使用查询调用AsNoTracking,要么自己手动新建Category 的实例。在任何一种情况下,该实例都与上下文分离。要再次附加它,您只需执行以下操作:

    context.Attach(category);
    

    但是,您无法直接访问此处的上下文。这也是您不应该将存储库模式与 EF 一起使用的又一个原因。由于糟糕的建议或错误地尝试按照他们习惯的方式做事,开发人员在一年中遭受了如此多的痛苦和折磨。

    EF 是一个 ORM(对象关系映射器),这是一种花哨的说法,它本身是一个数据层。 DbContext 是工作单元,每个 DbSet 都是一个存储库......已经。存储库模式用于抽象低级数据库访问(例如,构建 SQL 字符串的所有杂物)。 EF 已经是一个高级抽象,试图将其塞进另一个存储库模式层只会削弱它并导致像您在这里遇到的问题。

    总而言之,问题是category 是分离的。您需要首先确保它永远不会分离(例如,不要使用AsNoTracking)或找到一种方法来确保它稍后重新连接。但是,您最好的选择是完全丢弃所有这些存储库垃圾,直接使用上下文。选择使用像 EF 这样的 ORM 只是选择使用第三方 DAL,而不是自己编写。无论如何,写你自己的,最重要的是是错误的。您使用 ASP.NET Core 中的内置路由框架。您使用内置的模板引擎(即 Razor)。你觉得有必要对这些进行一些抽象吗?当然不是,那为什么 DAL 有什么不同呢?如果您只是必须创建一个抽象,那么请使用一个有意义的抽象,例如 CQRS、服务层或微服务模式。

    【讨论】:

    • 感谢您的回答。唉,这不是我的创作,我只是在上面做实现,我无法摆脱存储库。我没有手动更新类别,我没有使用 AsNoTracking 或任何类似的东西,它来自 DbContext,因此它不应该被分离。这里有一些更深层次的问题,这就是为什么我包含了所有似乎相关的信息。进一步的测试表明,即使使用Include(category =&gt; category.User) 也不能解决问题。相反,它不会在 Category 上引发重复键错误,而是在 AspNetUsers 上这样做。
    • 哦,我同意你的设计意见,但同样,我与这个设计或实现无关,我只需要处理它。如果是对我来说,我不会用 10 英尺的杆子碰 EF,而只使用 Dapper。这将意味着更多的工作,但也意味着更多的控制和轻松利用数据库特定(PG,在本例中)SQL 功能的能力,不要忘记找出问题是多么容易。跨度>
    猜你喜欢
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-18
    相关资源
    最近更新 更多