【问题标题】:How to model POCO object from a multilanguage database with Entity Framework 4?如何使用 Entity Framework 4 从多语言数据库中建模 POCO 对象?
【发布时间】:2011-08-19 23:28:15
【问题描述】:

我正在从头开始构建一个新项目。 我创建了一个数据库,我一直应用一个数据库结构,我用一个简短的不言自明的例子来解释:

表项->(ID,名称)->包含一般信息

Table ItemInfo -> (Item_Id, Language, Description) -> 包含语言相关信息。

Id 和 Item_Id 是通过外键关系连接起来的。

我的想法是以一种最终只使用通过实体框架填充的单个 POCO 对象“项目”的方式对其进行建模。该对象将仅包含公共属性:Id、Name 和 Description。 语言将对使用此对象的代码隐藏,对象本身应负责根据包含该语言的全局变量给出正确的描述。

我尝试了几种方法来做到这一点,但总是遇到问题,因为实体框架不允许这种情况。我总是必须检索所有语言的信息,而不仅仅是当前的一种或使用 2 个不同的查询。

所以最后我开始使用的解决方案是让 T4 模板同时创建 Item 和 ItemInfo,然后我手动添加了类似这样的代码:

public partial class Item
{
    private ItemInfo _itemInfo = null;
    private ItemInfo itemInfo
    {
        get
        {
            if (_itemInfo == null) _itemInfo = ItemInfoes.Single(p => p.Language == GlobalContext.Language);
            return _itemInfo;
        }
    }
    public Description 
    {
        get { return itemInfo.Description; } 
        set { itemInfo.Description = value;}
    }
}

使用此代码,我将 ItemInfo 中的其他属性添加到 Item 并根据我的要求选择了正确的语言。 你认为这是一个好的解决方案吗?你会如何解决这个问题?

但是,运行 sql profiler 我可以看到 2 个不同的 sql 查询用于填充 Item 对象,一个查询 Item 表,另一个查询 ItemInfo。

可以通过在 2 个表之间进行连接的单个查询来实现相同的场景吗? (我担心长期性能受到影响,这也是我在没有 ORM 的情况下会这样做的方式。

欢迎任何建议,我有多年的编程经验,但我是实体框架和 ORM 的新手。

请帮忙。

【问题讨论】:

  • 为什么不使用 linq-to-sql 而不是使用实体框架?
  • 另外,我假设一个 Item 有很多 ItemInfos?
  • linq to sql 已过时,将被淘汰 linq to entity 将在很长一段时间内得到支持,并且正在积极改进和开发中,如果可能,请避免使用 linq to sql

标签: c# entity-framework entity-framework-4 linq-to-entities multilingual


【解决方案1】:

您没有展示如何获取Item 对象,但通常我认为在一个查询中获取所有内容没有问题。您有多种选择。
您可以进行投影(但不能投影到映射实体上 - 在本例中我投影到匿名对象上):

context.
Items.
Select(item => new 
               {
                   Id = item.Id,
                   Name = item.Name,
                   Description = item.
                                 ItemInfo.
                                 Where(info => info.Language == YourGlobalLang).
                                 Select(info => info.Description).
                                 FirstOrDefault()
               };

(已编辑为使用FirstOrDefault 而不是Single - 请参阅@Craig Stuntz 的评论讨论)

这将返回所有Items 的列表 - 您可以添加Where 子句进行过滤。

或者你可以反过来获取它(以ItemInfo开头):

ItemInfo itemInfo = context.
                    ItemInfoes.
                    Include(info => info.Item).
                    SingleOrDefault(info => info.Language == YourGlobalLang && 
                                            info.Item.Id == itemIdToFetch);

之后您可以访问项目对象本身:

Item item = itemInfo.Item;

【讨论】:

  • Single() 将在 L2E 运行时失败。你必须改用SingleOrDefault()
  • @Craig Stuntz - 这取决于 OP 将如何处理错误情况,所以我不会明确指出 Single 是错误的,而 SingleOrDefault 是正确的。这也超出了问题的范围,但你当然有一点。在第一个示例中,我可能希望抛出异常,因为在同一语言中没有或不止一个翻译是无效状态。然而,在第二个示例中,我完全同意 - SingleOrDefault 更可取,因为我们正在尝试获取具有特定 Id 的项目并且它可能不存在。编辑了第二个例子,谢谢!
  • 我没有说Single() 是“错误的”。我说过它不会在 L2E 查询中执行。曾经。即使只有一种语言。你试过了吗?
  • @Craig Stuntz - 是的,我在 L2E 查询中使用了 Single(),它运行良好(至少在 EF4 中)。是否有任何您担心无法解决的特定场景?
  • @Craig Stuntz - 好的,我只是做了更多的测试,你部分正确。但是,您也不能使用SingleOrDefault。这是一个例外:“The methods 'Single' and 'SingleOrDefault' can only be used as a final query operation. Consider using the method 'FirstOrDefault' in this instance instead.”。所以在第一个例子中它确实会失败。编辑为改用FirstOrDefault,再次感谢。
【解决方案2】:

我会说这是一种合理的方法。此外,我不会担心两个简单选择的性能问题。如果将来证明是一个问题,您可以将其更改为视图。

【讨论】:

  • +1 建议不要过早地担心性能。如果确实发现是瓶颈,请优化代码。如果您的应用程序运行缓慢,则很可能某处的某些 UI 代码才是真正的罪魁祸首。
  • 这是一个瓶颈。这是对现有应用程序的重写,当前的瓶颈确实是数据库。有 15 个具有这种结构的表,因此总共 30 个。此外,语言的数量将从 3-4 开始,但可能会增加到 10。每次检索一个项目时使用 2 个查询将有效地减慢每个页面加载项目需要从不同的表中提取,以后很难更改。
  • 如果确实如此,那么我建议使用 Yakimych 的第一种方法。您可能希望使用类型化的包装类 (WrappedItem) 而不是匿名类型,并在任何地方引用它而不是 Item 本身。
  • 这里会有一个真正的性能问题,因为您需要 N+1 DB 查询来填充任何列表。这(真的)很糟糕,修复它并不是过早的优化。
【解决方案3】:

您可以尝试动态添加 where 子句。 或者说直接使用linq to sql。

How to append a where clause to an Entity Framework ObjectSet

Add the where clause dynamically in Entity Framework

【讨论】:

    猜你喜欢
    • 2011-04-18
    • 2023-03-27
    • 2011-07-26
    • 2012-10-30
    • 1970-01-01
    • 2011-07-19
    • 2011-12-28
    • 2012-11-08
    • 2011-03-31
    相关资源
    最近更新 更多