【问题标题】:EF Context in Model Class模型类中的 EF 上下文
【发布时间】:2017-11-25 20:04:33
【问题描述】:

在我们的 Web.API 项目中,我们使用 Entity Framework 6。我们有一个包含 DatabaseContext 的 DataModel 类。 DataModel 是 [ThreadStatic] Singleton。

像这样:

public class DataModel
{
    [ThreadStatic] private static DataModel _instance;
    public static DataModel Instance => _instance ?? (_instance = new DataModel());

    public DatabaseContext Context { get; private set; }

    private DataModel()
    {
        Context = NewContext();
    }
}

现在在我们的一个(部分)模型类中,我们使用如下上下文:

public partial class Job
{
    public User CreatedByUser
    {
        get { return DataModel.Instance.Context.Users.FirstOrDefault(d => d.Username == CreatedBy); }
    }
}

我们在数据库的另一个表中搜索相应的用户。这可行,但在我看来,这不是一个漂亮的解决方案。特别是如果我们计划将项目迁移到 .NET Core 并为数据库上下文使用依赖注入。

我的问题是,有没有一种模式可以更优雅地解决问题?依赖注入在这里不起作用,因为 Entity Framework 生成模型对象。或者,如果我们将此代码从部分类移动到例如,它会更好吗?一个实用类?但是我们怎样才能在那里注入上下文呢?

【问题讨论】:

  • 单例上下文和在实体类中使用上下文都是反模式。所以首先尝试摆脱这些模式,而不用担心 DI。您会看到,在那之后,将代码转换为 DI 会容易得多。 如何做到这一点在很大程度上取决于你的架构的其余部分,无法说出任何明智的说法。
  • 这个数据模型位于哪一层?是 API 返回的模型,还是数据库抽象层的尝试?一般来说,我建议使用存储库模式,但这并不适合所有问题。让模型通过数据库调用(“活动记录模式”)自行填充通常会导致比它解决的问题更多的问题。
  • 使用正确的外键可以解决问题。作业与 CreatedBy 用户 on UserId 相关,而不是用户名(除非用户名是您的用户表中的 PK,在这种情况下您应该重新考虑)。在 EF 中映射该关系,然后您可以在附加实体时使用延迟加载导航到 CreatedByUser,或者使用查询中的 Include 子句立即检索它。您当前的解决方案不是线程安全的,尤其是考虑到您有一个具有多个线程的 asp.net 应用程序(至少 1x 并发请求)。
  • 作业模型由 EF 创建。我们已经讨论过使用外键,但我们决定反对它。但现在,使用它更有意义。我们将修复反模式。谢谢!

标签: c# entity-framework dependency-injection


【解决方案1】:

您通常希望避免要求您的模型了解它们的创建和使用上下文,因为这些知识应该是自上而下而不是自下而上的。除其他原因外,您还会遇到现在遇到的设计问题。您可以尝试在应用程序级别使用扩展方法、控制框架反转、实用方法、pocos 等来设计解决它的方法,但归根结底,您正在尝试解决一个仅存在于您的底层的问题数据库架构的设计不足以满足您的预期用途。

例如,您的Job 表引用了创建它的用户的用户名。为什么?用户名可能会更改,并且任何时候您想要该用户的其他关键属性都需要执行辅助查找(就像您在部分类中所做的那样)。如果您改为让Job 表维护User 表的外键,则只要在查询中包含适当的相关实体,您就可以使用C# 端的导航属性来获取完整的用户对象。您甚至不需要分部类,并且您的数据库架构将变得更易于维护,作为额外的好处。

有时最好简化数据库设计以简化代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多