我有以下 Code First 模型(尽管实际上我只是使用 EF 访问现有数据库的一个子集;为简洁起见,此示例中省略了表和列属性):
public enum AlphaState { Unknown = '\0', Good = 'G', Bad = 'B' }
public class Alpha
{
[Key]
public long AlphaIndex { get; set; }
public string Deleted { get; set; }
public AlphaState State { get; set; }
[InverseProperty("Alpha")]
public ICollection<Bravo> Bravos { get; set; }
}
public class Bravo
{
[Key]
public long BravoIndex { get; set; }
[ForeignKey("Alpha")]
public long? AlphaIndex { get; set; }
public virtual Alpha Alpha { get; set; }
[InverseProperty("Bravo")]
public ICollection<Charlie> Charlies { get; set; }
}
public class Charlie
{
[Key]
public int CharlieIndex { get; set; }
public string Deleted { get; set; }
public DateTime CreatedAt { get; set; }
[ForeignKey("Bravo")]
public long BravoIndex { get; set; }
public virtual Bravo Bravo { get; set; }
[ForeignKey("Delta")]
public long DeltaIndex { get; set; }
public virtual Delta Delta { get; set; }
[InverseProperty("Charlie")]
public virtual ICollection<Delta> AllDeltas { get; set; }
}
public class Delta
{
[Key]
public long DeltaIndex { get; set; }
[ForeignKey("Charlie")]
public long CharlieIndex { get; set; }
public virtual Charlie Charlie { get; set; }
[InverseProperty("Delta")] // actually a 1:0..1 relationship
public ICollection<Echo> Echoes { get; set; }
}
public enum EchoType { Unknown = 0, One = 1, Two = 2, Three = 3 }
public class Echo
{
[Key]
public int EchoIndex { get; set; }
public EchoType Type { get; set; }
[ForeignKey("Delta")]
public long DeltaIndex { get; set; }
public virtual Delta Delta { get; set; }
}
当我尝试这个查询时:
var result = context.Alphas.Where(a => a.State == AlphaState.Good)
.Where(a => a.Deleted != "Y")
.Where(a => a.Bravos.SelectMany(b => b.Charlies)
.Where(c => c.Deleted != "Y")
.Where(c => c.Delta.Echoes.Any())
.OrderByDescending(c => c.CreatedAt).Take(1)
.Any(c => c.Delta.Echoes.Any(e => e.Type == EchoType.Two)))
.Select(a => a.AlphaIndex);
...或这个等效查询(B->A 和 D->E 是 1:0..1 关系):
var result = context.Bravos.Where(b => b.Alpha != null)
.Where(b => b.Alpha.State == AlphaState.Good)
.Where(b => b.Alpha.Deleted != "Y")
.Where(b => b.Charlies.Where(c => c.Deleted != "Y")
.Where(c => c.Delta.Echoes.Any())
.OrderByDescending(c => c.CreatedAt)
.FirstOrDefault().Delta.Echoes
.Any(e => e.Type == EchoType.Two))
.Select(b => b.AlphaIndex);
...由于生成的 SQL 语句中存在问题,我得到了一个异常。具体来说,它试图在多嵌套子查询中使用 Alpha 表的声明标识符,并且如 here 所述, Oracle 不会将标识符映射到超过 1 层深度。
我是不是疯了,还是漏掉了一些明显的东西? Oracle 是否认真地提供了一个 EF 提供程序,该提供程序将生成他们自己的数据库无法运行的查询以进行合理的(尽管公认不是微不足道的)查询?
是否有可以设置的配置选项(在数据库或 EF 中)对此有帮助?是否有某种策略可以用来以不同方式构建查询或拆分它们来解决此问题(不会通过将单次往返转换为多次来降低性能)?
请注意,我可以自己编写一个 SQL 语句来获取相同的信息:
select A.alpha_index
from ALPHA A
inner join BRAVO B on B.alpha_index = A.alpha_index
inner join CHARLIE C on C.bravo_index = B.bravo_index
inner join ECHO E on E.delta_index = D.delta_index
where (A.deleted is null or A.deleted <> 'Y')
and A.state = 'G'
and E.type = 2
and C.created_at = (select max(C2.created_at)
from CHARLIE C2
inner join ECHO E2 on E2.delta_index = C2.delta_index
where (C2.deleted is null or C2.deleted <> 'Y')
and C2.bravo_index = C.bravo_index)
我想使用 EF 制作一个模块化系统,允许不懂任何 SQL 的用户使用我提供的构建 block 构建自己的查询;每个“ block ”都会有一个 Expression<Func<Model, bool>>
可以将其放入 Where 子句链中以构建查询。这样做的全部意义在于避免必须为用户可能想要查找的所有内容编写实际的 SQL 查询。
最佳答案
问题不在于 Oracle。 EF Provider for Oracle 生成 EF 向其询问的小块查询。 EF 在这方面效率不高。
无论如何,在这种情况下,您有两种不同的方法。
您可以尝试简化查询(就像您写的那样)。
另一种方法是使用从 SQL 查询(而不是 LINQ 查询)开始的不同映射器来具体化实体。我知道 Dapper,但实际上它只适用于只读目的,并且有其他一些限制。
我不知道什么是最糟糕的...
关于c# - Entity Framework 与 Oracle 嵌套查询限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30718102/