【发布时间】:2023-03-24 21:58:01
【问题描述】:
免责声明:这些要求不是我设定的,除非这是不可能完成的任务,否则我无法说服我的老板。
假设我们有两个实体:Item 和 ItemTranslation。
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; }
}
要求Item.Description应根据默认选择的语言填写,但也允许根据用户的需要指定。 Item.Description 列实际上并不存在于数据库中。
在 SQL 中这很容易:您所要做的就是像这样查询两个表
SELECT [Item].[Id], [ItemTranslation].[Description], [Item].[ParentId]
FROM [Item]
LEFT JOIN [ItemTranslation] ON [Item].[Id] = [ItemTranslation].[ItemId]
WHERE [CultureId] = {cultureId}
或者根据您的实现使用 OUTER APPLY。我已将此查询添加到 Entity Framework 中内置的 .FromSql() 函数中。
将所有这些放在一个 OData API 中,这对于一个 Item 来说一切正常。但是,一旦您开始使用 $expand(在幕后是一种 .Include()),它就不再起作用了。为相关实体发送到数据库的查询不再包含我在.FromSql() 中指定的 SQL。只有第一个查询可以。除此之外,当您从不同的控制器查询Item 时,例如ItemTranslation 这也将不再起作用,因为 .FromSql() 仅应用于另一个控制器。
我可以编写一个查询拦截器,它简单地将生成的 SQL 替换为实体框架,并将 FROM [Item] 替换为 FROM [Item] LEFT JOIN [ItemTranslation] ON [Item].[Id] = [ItemTranslation].[ItemId] WHERE [CultureId] = {cultureId},但我想知道是否有比这更好的实现。甚至可能对模型进行重新设计。我愿意接受建议。
【问题讨论】:
-
该 SQL 实际上是无效的,因为
Description和Id在两个表之间都是不明确的。 -
您为什么要放弃以更传统的方式使用 EF 生成的 SQL? (...或者你为什么使用
FromSql而不是 LINQ?) -
...您的需求中似乎没有任何内容需要回退到制作自己的 SQL。正如您所发现的那样,这样做有缺点。
-
@spender 什么意思,我必须根据用户传递的 CultureId 填写 Item.Description。如果没有 .FromSql() 并且在 OData API 上下文中,我将如何实现这一点,这意味着我必须返回 IQueryable
? -
您知道针对 EF 的 LINQ 查询也会返回
IQueryable<T>?
标签: c# entity-framework odata