EntityFramework在发送的查询中不包含where子句

时间:2019-11-20 10:47:02

标签: c# .net entity-framework-6

我有一个国际化的数据库,其中有一个Collection表,一个i18n表和一个translation表。

collection名称字段包含i18n表中的GUID,而translation表包含每个语言环境的翻译列表。

这是我在C#中使用的代码:

var ctx = new CollectEntities();
var colls = ctx.collections.Include(x => x.i18n);

foreach(var c in colls)
{
    var t = c.i18n.translations.Where(x => x.locale_id == "fr").FirstOrDefault();
    MessageBox.Show(t.trans_text);
}

这是生成的SQL查询:

SELECT 
    1 AS [C1], 
    [Extent1].[coll_id] AS [coll_id], 
    [Extent1].[coll_name] AS [coll_name], 
    [Extent2].[i18n_id] AS [i18n_id], 
    [Extent2].[i18n_default] AS [i18n_default]
    FROM  [dbo].[collection] AS [Extent1]
    INNER JOIN [dbo].[i18n] AS [Extent2] ON [Extent1].[coll_name] = [Extent2].[i18n_id]
-- Executing at 20/11/2019 11:39:12 +01:00
-- Completed in 17 ms with result: SqlDataReader

SELECT 
    [Extent1].[trans_id] AS [trans_id], 
    [Extent1].[i18n_id] AS [i18n_id], 
    [Extent1].[locale_id] AS [locale_id], 
    [Extent1].[trans_text] AS [trans_text]
    FROM [dbo].[translation] AS [Extent1]
    WHERE [Extent1].[i18n_id] = @EntityKeyValue1
-- EntityKeyValue1: '929ba17e-c6c0-43ff-a8bc-6efa950fa03d' (Type = Guid, IsNullable = false)

如果我有50种翻译,这是浪费时间和交通。为什么不生成:

    WHERE [Extent1].[i18n_id] = @EntityKeyValue1 AND [Extent1].[locale_id] = @the_locale_I_want

我想念什么?

编辑:出于这个问题的目的,我简化了代码。我知道,如前所述,直接获取trans_text字段的列表很有意义。

但是在“现实世界中”,每个translation对象至少具有两个属性(文本和图片),每个collection对象具有其他所需的属性。因此,仍然需要遍历collections。哦,翻译将永远存在。

我想要实现的是,使用一个查询已加载的适当翻译来检索所有集合。

让我添加一个示例进行说明: 没有EF的“旧式” SQL查询将类似于:

SELECT collection.*, i18n.*, translation.*
FROM collection
    INNER JOIN i18n ON i18n.i18n_id=collection.coll_name
    LEFT OUTER JOIN translation ON translation.i18n_id=i18n.i18n_id
        AND translation.locale_id = 'fr'

要使用的代码为:

var ctx = new CollectEntities();
var colls = ctx.collections.Include(x => x.i18n)[.something to catch translation];

foreach(var c in colls)
{
    var t = c.i18n.translations.Where(x => x.locale_id == "fr").FirstOrDefault();
    MessageBox.Show($"{c.coll_id}, price={c.coll_price}, name is {t?.trans_text ?? c.i18n.i18n_defaulttext}, picture file is {t?.trans_picturefilename}");
}

4 个答案:

答案 0 :(得分:2)

您正在枚举循环中的collections查询,然后为每个实例获取翻译。您可以在一个查询中完成相同的操作。

var ctx = new CollectooEntities();
var dto = ctx.collections.Select(x => new {
        coll_id = x.coll_id,
        coll_price = x.coll_price,
        i18n_defaulttext = x.i18n.i18n_defaulttext,
        trans = x.i18n.translations
            .Where(t => t.locale_id == "fr")
            .Select(t => new { trans_text, trans_picturefilename })
            .FirstOrDefault()
    });

foreach(var c in dto)
{
    MessageBox.Show($"{c.coll_id}, price={c.coll_price}, name is {c.trans?.trans_text ?? c.i18n_defaulttext}, picture file is {c.trans?.trans_picturefilename}");
}

答案 1 :(得分:1)

尝试将集合设为IQueryable,这样它将在服务器端执行查询,包括添加的过滤器。

即加上

Where(x => x.locale_id == "fr").FirstOrDefault();

答案 2 :(得分:0)

包含方式

您需要在此处使用Query()

Loading Related Entities

  

在明确加载相关实体时应用过滤器

     

Query方法提供对在加载相关实体时Entity Framework将使用的基础查询的访问。然后,您可以在调用LINQ扩展方法(例如ToList,Load等)执行该查询之前,使用LINQ将过滤器应用于查询。该Query方法可以与引用导航属性和集合导航属性一起使用,但对于其中的集合最有用它只能用于加载集合的一部分。例如:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Load the posts with the 'entity-framework' tag related to a given blog.
    context.Entry(blog)
           .Collection(b => b.Posts)
           .Query()
           .Where(p => p.Tags.Contains("entity-framework"))
           .Load();

没有包含方式

您似乎没有为任何特定的集合进行过滤,那么为什么不直接加载翻译呢?

forach( t in ctx.translations.Where(x => x.locale_id == "fr"))
         MessageBox.Show(t.trans_text);

如果要添加过滤器,则可以

var t = ctx.translations.Where(x => x.locale_id == "fr" && i18n_id = ...))
MessageBox.Show(t.trans_text);

答案 3 :(得分:0)

基于其他答案,我找到了一种将原始对象保留在查询结果中的解决方案,以保持从数据库中获取新字段以在将来的EF模型同步中可用的优势:

var ctx = new CollectEntities();

var colls = ctx.collections
    .Select(x => new { Obj = x, x.i18n, trans = x.i18n.translations.Where(t => t.locale_id == "fr").FirstOrDefault() });

foreach (var c in colls2)
{
    MessageBox.Show($"{c.Obj.coll_id}: {c.trans?.trans_text ?? c.Obj.i18n.i18n_default}");
}

这样,Obj属性包含原始collection对象,而trans包含translation对象(如果适用)。我必须在结果对象中添加x.i18n才能强制EF加载i18n导航属性,因为返回新对象类型的Select方法取消了Include指令。