【问题标题】:Write a translatable method for LINQ TO Entities为 LINQ TO 实体编写可翻译的方法
【发布时间】:2018-01-29 08:55:08
【问题描述】:

使用实体框架(LINQ to Entities)

以下工作正常。表达式被翻译成 SQL

var foos = ctx.Foos.Select(f => new {
   P1 = ctx.Bars.FirstOrDefault(b => b.SomeProp == "Const1" && f.X1 == b.Y),
   P2 = ctx.Bars.FirstOrDefault(b => b.SomeProp == "Const2" && f.X2 == b.Y),
   P3 = ctx.Bars.FirstOrDefault(b => b.SomeProp == "Const3" && f.X3 == b.Y),
}

重复表达式b.SomeProp == "..." && f.X* == b.Y实际上是真实表达式的简化版本,但如果你能帮助我理解这一点。剩下的我也会想办法的……

我想写的是这样的。 (首选)

var foos = ctx.Foos.Select(f => new {
   P1 = f.GetBar("Const1", f.X1),
   P2 = f.GetBar("Const2", f.X2),
   P3 = f.GetBar("Const3", f.X3),
}

但我也可以接受类似的东西

P1 = ctx.Bars.GetByFoo(f.X1, "Const1");
- or -
P1 = ctx.Bars.FirstOrDefault(GetByFoo(f.X1, "Const1"))
- or -
P1 = ctx.Bars.GetByFoo(x => x.X1, "Const1");

基于这个答案https://stackoverflow.com/a/2244917/2968001到目前为止我最接近的是

ctx.Bars.FirstOrDefault(GetByFoo(x => x.Y == f.X1 , "Const1"))

and

private static Expression<Func<Bar, bool>> GetByFoo(Func<Foo, bool> optionSelector, string par1)
    {
        return b => b.SomeProp == par1 && optionSelector(o);
    }

不幸的是,这 a) 仍远未达到预期 b)这不起作用:(。它给出了一个运行时异常:

从范围 '' 引用的类型为 'Foo' 的变量 'f',但它不是 定义

表达式保持可翻译是很重要的。我不适合检索所有 foo,然后检索每个 foo 的 Bar。

【问题讨论】:

  • 问题是一切都是select lambda表达式树的一部分,因此不可执行也不可翻译,因为翻译是基于知识的,因此无法支持未知方法。
  • 1/2/3 字段(在我看来)就像您试图将其用作 FK。如果是这种情况,那么您为什么不使用 EF 的默认导航属性方法呢?您无需手动将两个实体连接在一起,EF 可以为您完成。
  • @Flater 他们几乎是 FK。 Bar.Y 不是唯一的,但是 Y 和 SomeProp 的组合是唯一的。但是属性 X1、X2、X3 并不“知道” SomeProp 值,这就是为什么我仍然需要提供它。

标签: c# entity-framework linq linq-to-entities expression


【解决方案1】:

这些方面的东西怎么样

public static class Extensions
{
    public static IQueryable<T> Filter<T>(this DemoContext instance, Expression<Func<T, bool>> predicate = null, string value = null)
        where T : class, IMarker
    {
        return instance
            .Set<T>()
            .Where(p => p.SomeProp == value)
            .Where(predicate);
    }
}

这应该允许你写类似的东西

var inner1 = ctx.Filter<Bar>(value: "Const1");
var inner2 = ctx.Filter<Bar>(value: "Const2");
var inner3 = ctx.Filter<Bar>(value: "Const3");

var query = from foo in ctx.Foos
            select new
            {
                P1 = inner1.FirstOrDefault(p => p.Y == foo.X1),
                P2 = inner2.FirstOrDefault(p => p.Y == foo.X2),
                P3 = inner3.FirstOrDefault(p => p.Y == foo.X3),
            };

IMarker 接口只是为了帮助您处理专门的谓词

public class Foo : IEntity
{
    public string X1 { get; set; }
    public string X2 { get; set; }
    public string X3 { get; set; }

}

public class Bar : IMarker
{
    public string SomeProp { get; set; }
    public string Y { get; set; }
}

public interface IEntity
{
}

public interface IMarker : IEntity
{
    string SomeProp { get; set; }
}

【讨论】:

  • 不错的尝试,但不行。这也给出了:LINQ to Entities 无法识别方法 '....Find...' 方法,并且此方法无法转换为存储表达式。
  • 确实,您可能想改用IQueryable 内部查询。查看我的更新以获取隔离特定于 Bar 的查询定义并稍后在查询中应用进一步过滤的示例
  • 好吧,内部查询可能会做到这一点。与此同时,我用查询语法和“let”重写了它。可能与使用内部查询相同。我试试看。
猜你喜欢
  • 2020-10-26
  • 2021-11-05
  • 2019-08-15
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多