【发布时间】:2015-08-19 13:13:51
【问题描述】:
我有一个复杂的 IQueryable,我希望 EF 使用单个数据库查询填充它,以便我可以在延迟执行的情况下使用它。请考虑以下示例。
对于这些型号:
public enum AlphaState { Unknown = '\0', Good = 'G', Bad = 'B' }
[Table("MY_ALPHA")]
public class Alpha
{
[Key]
[Column("alpha_index")]
public long Index { get; set; }
[Column("alpha_id")] // user-editable field that users call ID
public string AlphaId { get; set; }
[Column("deleted")]
public char? Deleted { get; set; }
[Column("state")]
public AlphaState State { get; set; }
[InverseProperty("Alpha")]
public ICollection<Bravo> Bravos { get; set; }
}
[Table("MY_BRAVO")]
public class Bravo
{
[Key]
[Column("bravo_index")]
public long BravoIndex { get; set; }
[ForeignKey("Alpha")]
[Column("alpha_index")] // actually a 1:0..1 relationship
public long? AlphaIndex { get; set; }
public virtual Alpha Alpha { get; set; }
[InverseProperty("Bravo")]
public ICollection<Charlie> Charlies { get; set; }
}
[Table("MY_CHARLIE_VIEW")]
public class Charlie
{
[Key]
[Column("charlie_index")]
public int CharlieIndex { get; set; }
[Column("deleted")]
public char? Deleted { get; set; }
[Column("created_at")]
public DateTime CreatedAt { get; set; }
[ForeignKey("Bravo")]
[Column("bravo_index")]
public long BravoIndex { get; set; }
public virtual Bravo Bravo { get; set; }
[ForeignKey("Delta")]
[Column("delta_index")]
public long DeltaIndex { get; set; }
public virtual Delta Delta { get; set; }
[InverseProperty("Charlie")]
public virtual ICollection<Delta> AllDeltas { get; set; }
}
[Table("MY_DELTA")]
public class Delta
{
[Key]
[Column("delta_index")]
public long DeltaIndex { get; set; }
[ForeignKey("Charlie")]
[Column("charlie_index")]
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 }
[Table("MY_ECHOES")]
public class Echo
{
[Key]
[Column("echo_index")]
public int EchoIndex { get; set; }
[Column("echo_type")]
public EchoType Type { get; set; }
[ForeignKey("Delta")]
[Column("delta_index")]
public long DeltaIndex { get; set; }
public virtual Delta Delta { get; set; }
}
...考虑这个查询:
IQueryable<Alpha> result = context.Alphas.Where(a => a.State == AlphaState.Good)
.Where(a => !a.Deleted.HasValue)
.Where(a => a.Bravos.SelectMany(b => b.Charlies)
.Where(c => !c.Deleted.HasValue)
.Where(c => c.Delta.Echoes.Any())
.OrderByDescending(c => c.CreatedAt).Take(1)
.Any(c => c.Delta.Echoes.Any(e => e.Type == EchoType.Two)))
var query = result as System.Data.Objects.ObjectQuery;
string queryString = query.ToTraceString();
注意: Charlie 实际上是一张桌子上的视图; Delta 对 Charlie 的表有一个 FK,但视图为链接到该 Charlie 的最新 Delta 提供了一个假 FK,因此模型使用它是因为计划仅将 EF 用于查询,从不用于更新。
我希望这个查询由单个数据库查询填充,但正如所写的那样,这不是正在发生的事情。如何修改此查询以获得相同的结果,但让 EF 简单地将条件构建到 results IQueryable 中,而不是为其预取数据?
我怎么知道它使用两个查询
我确定它会拆分为多个查询,因为出于此问题范围之外的原因,我故意为上下文提供了错误的连接字符串。 result 是一个 IQueryable,因此它应该使用延迟执行,并且在使用它之前实际上不会尝试检索任何数据,但是我一声明它就会收到连接失败异常。
背景
我们有一个现有的数据库结构、数据库访问层和数十万行使用上述结构和 DAL 的代码。我们想添加一个 UI 以允许用户构建自己的复杂查询,而 EF 似乎是为此构建底层模型的好方法。不过,我们以前从未使用过 EF,所以当权者宣布它永远无法连接到数据库;我们应该使用 EF 生成一个 IQueryable,从中提取查询字符串,然后使用我们现有的 DAL 来运行查询。
【问题讨论】:
-
您可能想使用以下方法检查您的假设:stackoverflow.com/a/20751723/4812586 或 msdn.microsoft.com/en-us/data/dn469464.aspx
-
你可以在没有子查询的情况下在 SQL 中做到这一点吗?
-
我相信EF会在你访问
ObjectQuery属性时连接到数据库。它这样做不是为了执行查询,而是为了确定您正在使用的 SQL Server 版本,以便能够为实体连接字符串提供ProviderManifestToken值。这是 EF 早期的遗留问题。 -
@ObliviousSage 对此进行调查。使连接有效,以免出现异常。然后使用 SQL Profiler 查看正在执行的内容。也许这只是一个真正的查询。
-
默认情况下,EF 不会立即将您的
IQueryable<>转换为提供程序查询。然而,各种方法迫使它这样做,包括任何输出 T-SQL 的跟踪方法。
标签: c# entity-framework entity-framework-6 iqueryable deferred-execution