【问题标题】:Is it good to use a static EF object context in an MVC application for better perf?在 MVC 应用程序中使用静态 EF 对象上下文以获得更好的性能是否很好?
【发布时间】:2012-02-23 10:20:22
【问题描述】:

让我们从这个基本场景开始:

我有一堆基本上很少更改的表 Enums(例如 GeoLocations、Category 等)我想将它们加载到我的 EF ObjectContext 中,以便我可以将它们分配给将它们引用为 FK 的实体。这些对象还用于填充各种下拉控件。到目前为止相当标准的场景。

由于在 MVC 中为每个页面请求创建了一个新的控制器,因此创建了一个新的实体上下文并重复加载这些“枚举”对象。我考虑过在所有控制器实例(或存储库对象)中使用静态上下文对象。

但这是否需要过多的锁定,因此实际上会降低性能?

另外,我正在考虑仅对只读表使用静态上下文。但是由于引用它们的实体无论如何都必须在相同的上下文中,所以这与上述没有任何不同。

我也不想涉足附加/分离这些枚举对象的业务。因为我相信一旦我将一个静态枚举对象附加到一个实体上,我就不能再将它附加到另一个实体上??

请帮忙,我对 EF + MVC 还很陌生,所以想知道最好的方法是什么。

【问题讨论】:

  • 非常有趣的问题,可能是一个让我今晚很晚睡不着觉的问题,调查是否可以做到。

标签: asp.net-mvc entity-framework


【解决方案1】:

就我个人而言,我从来没有任何静态上下文的东西,等等。对我来说,当我调用数据库 (CRUD) 时,我将该上下文用于单个事务/工作单元。

因此,在这种情况下,您的建议是您希望从数据库中检索一些数据 .. 而这些数据.. 或多或少 .. 只读且不会更改/静态。

查找数据就是一个很好的例子。

因此您的类别永远不会改变。您的地理位置也永远不会改变。

我不会在数据库/持久性级别担心这个概念,而是在应用程序级别。所以,忘记这些数据是静态的/只读的等等。然后得到它。然后,当您在您的应用程序中(即 ASP.NET Web MVC 控制器方法或在 global.asax 代码中)时,您应该在 UI 层缓存这个 ...。

如果你正在做一个不错的 n 层 MVC 应用程序,其中包含

  • UI 层
  • 服务/业务逻辑层
  • 持久性/数据库数据层

然后我会将其缓存在中间层 .. 由 UI 层调用(即 MVC 控制器操作 .. 例如 public void Index())

我认为知道如何区分您的关注点很重要.. 数据库的东西应该只是 -> CRUD'ish 的东西和一些独特的存储过程(在需要时)。不用担心缓存数据等问题。保持这一层尽可能轻量和简单。

然后,您的中间层(如果存在)或顶层应该担心如何处理这些数据 -> 在这种情况下,缓存它,因为它是非常静态的。

【讨论】:

  • 感谢 Pure.Krome 的回答。如果我可以在其他 EF 上下文调用中使用缓存的 Categories EntitySet 作为引用,这是一个很好的解决方案。但我不能。如果 EF 像 Linq2SQL(或带有 FK 关联的 EF 4.0)一样工作,那么我可以简单地使用 PK ID,这就是故事的结尾。但不幸的是,我现在不能这样做。我必须为父实体分配一个有效的类别,以便在相关 EF 上下文中创建/更新调用。这很糟糕,但是您“应该”使用 EF 的方式...所以如果我要像这样使用 EF,如何最好地“缓存”这些数据,如类别?
  • 如果我没看错,您尝试做的是 1) 急切加载一些静态查找数据和/或 2) 延迟加载一些静态查找数据。在我看来,我将OFF 延迟加载。我个人讨厌这样。为什么?这意味着你还没有考虑过你的应用程序,也没有加载初始命中的所有数据......这对 IMO 来说很糟糕。分贝越少越好。所以,如果你需要一些包含子属性的对象..这是一些查找数据..那么也许我们可以解决这个问题。那么,你可以发布一些代码,所以我可以尝试操作它。我不能保证什么..但让我们看看..
【解决方案2】:

我使用 Linq2SQL 实现了类似的功能,方法是在应用启动时检索这些“查找表”作为列表并将它们存储在 ASP 的缓存机制中。通过使用 ASP 缓存,我不必担心线程/锁定等。不知道为什么需要将它们附加到上下文,如果需要,可以通过表 PK id 轻松检索类似的东西。

【讨论】:

    【解决方案3】:

    我相信这既是缓存内容的问题,也是如何缓存的问题。在处理 EF 时,当您尝试跨不同上下文持久化 EF 对象并尝试分离/附加这些对象时,您可能会很快遇到问题。如果您将自己的 POCO 对象与自定义 t4 模板一起使用,那么这不是问题,但如果您使用的是 vanilla EF,那么您将需要为缓存创建 POCO 对象。

    对于大多数简单的查找项(即数字主键和字符串文本描述),您可以使用 Dictionary。如果您有多个字段需要通过 UI 传递和返回,那么您可以构建更完整的对象模型。由于这些将是 POCO 对象,因此它们几乎可以在任何地方以您喜欢的任何方式持久化。我建议在 MVC 应用程序之外使用缓存逻辑,以便您可以轻松地模拟缓存活动以进行测试。如果您有多个列表需要缓存,您可以将它们全部放在一个容器类中,如下所示:

    public class MyCacheContainer
    {
        public Dictionary<int, string> GeoLocations { get; set; }
    
        public List<Category> Categories { get; set; }
    }
    

    下一个问题是您的实体模型中是否真的需要这些对象。您真正需要的只是主键(即,您使用字典中的键和值创建一个下拉列表,然后发布 ID)。因此,您可能会在构建视图模型时处理对文本描述的所有查找。这可能看起来像这样:

    MyEntityObject item = Context.MyEntityObjects.FirstOrDefault(i => i.Id == id);
    MyCacheContainer cache = CacheFactory.GetCache();
    MyViewModel model = new MyViewModel { Item = item, GeoLocationDescription = GeoLocations[item.GeoLocationId] };
    

    如果您绝对必须在您的上下文中拥有这些对象(即,如果存在将 2 个或更多其他表联系在一起的引用实体),您可以将该缓存容器传递到您的数据访问层,以便它可以进行正确的查找。

    至于分配“有效”实体,在 .Net 4 中,您只需设置外键属性,而不必实际附加对象(技术上您可以在 3.5 中执行此操作,但它需要使用魔术字符串来设置键)。如果您使用的是 3.5,则可以尝试以下操作:

    myItem.Category = Context.Categories.FirstOrDefault(c => c.id == id);
    

    虽然这不是最优雅的解决方案,并且确实需要额外往返数据库才能获得您并不真正需要的类别,但它确实有效。基于主键进行单条记录查找不应该真的有那么大的影响,特别是如果表像您正在谈论的查找数据类型一样小。

    如果您坚持使用 3.5 并且不想进行额外的往返,并且您想要使用魔术字符串路线,只需确保您为魔术字符串使用某种类型的静态资源和/或代码生成器,这样你不胖手指他们。这里有很多例子展示了如何在不进入数据库的情况下将新的 EntityKey 分配给引用,所以我不会在这个问题上讨论这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-04
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 2021-02-05
      • 1970-01-01
      相关资源
      最近更新 更多