EF FromSql()及其相关实体

时间:2018-08-16 11:42:44

标签: c# entity-framework odata

免责声明:我没有设定这些要求,除非这是一项不可能完成的任务,否则我不能说服老板。

假设我们有两个实体:ItemItemTranslation

public class Item
{
    public int Id { get; set; }

    public string Description { get; set; }

    public virtual ICollection<Item> Children { get; set; }
    public virtual Item Parent { get; set; }
    public virtual ICollection<ItemTranslation> Translations { get; set; }
}

public class ItemTranslation
{
    public int Id { get; set; }

    public string CultureId { get; set; }
    public string Description { get; set; }

    public virtual Item Item { get; set; }
}

要求是ItemDescription的填写应基于默认选择的语言,但也可以根据用户的需要指定。 ItemDescription列实际上不在数据库中。

在SQL中这很容易:您要做的就是像这样查询两个表

SELECT [Item].[Id], [ItemTranslation].[Description], [Item].[ParentId] 
FROM [Item] 
LEFT JOIN [ItemTranslation] ON [Item].[Id] = [ItemTranslation].[ItemId]  
WHERE [CultureId] = {cultureId}

或根据您的实现使用外部应用。我已将此查询添加到Entity Framework中内置的.FromSql()函数中。

将这些全部放到OData API中,并且对于一个Item来说一切正常。但是,一旦您开始使用$ expand(在幕后是一种.include()),它就不再起作用。发送到数据库的相关实体的查询不再包含我在.FromSql()中指定的SQL。只有第一个查询。最重要的是,当您从其他控制器查询Item时,例如ItemTranslation也将不再起作用,因为.FromSql()仅应用于其他控制器。

我可以编写一个查询拦截器,该拦截器仅用Entity Framework替换生成的SQL,并用FROM [Item]替换FROM [Item] LEFT JOIN [ItemTranslation] ON [Item].[Id] = [ItemTranslation].[ItemId] WHERE [CultureId] = {cultureId},但是我想知道是否有比这更好的实现。甚至可以重新设计模型。我愿意接受建议。

1 个答案:

答案 0 :(得分:0)

FromSql 有一些limitations。我怀疑这是 Include 无法正常工作的原因。

但是一旦使用EF,为什么要搞乱SQL?该查询有哪些困难使您无法在LINQ中进行查询?也许左联接?

from item in ctx.Items
from itemTranslation in ctx.ItemTranslations.Where(it => it.Item.Id == item.Id).DefaultIfEmpty()
where itemTranslation.CultureId == cultureId
select new { item.Id, itemTranslation.Description, ParentId = item.Parent.Id };

更新

再次讨论该问题,我看到了另一个问题。 Include 仅适用于 IQueryable ,其中T是其导航属性已正确映射的实体。现在,从这个角度来看,如果使用 FromSql 或LINQ生成某个投影的 IQueryable 而不是实体 Include ,则无所谓em>由于明显的原因无法正常工作。

要能够包含 ItemTranslation 实体,您的操作方法应如下所示:

[Queryable]
public IQueryable<Item> GetItems()
{
    return db.Items;
}

因此该框架可以对您返回的 IQueryable 执行 $ expand 。但是,这将包括所有项目的翻译,而不仅仅是具有所需文化的项目。如果我正确理解,这是您的核心问题。

很明显,您不能将此区域性过滤器应用于 IQueryable 。但是您不应该这样做,因为这是通过OData中的$filter实现的:

GET https://.../Items/$expand=Translations&$filter=Translations/CultureId eq culture