Linq to Entities中的条件包含是什么?

22

我觉得以下内容应该是可能的,我只是不确定应该采取什么方法。

我想要做的是使用include方法来塑造我的结果,也就是定义沿着对象图遍历的程度。但是...我希望这种遍历是有条件的。

something like...

dealerships
    .include( d => d.parts.where(p => p.price < 100.00))
    .include( d => d.parts.suppliers.where(s => s.country == "brazil"));

我知道这不是有效的linq代码,实际上它是极其错误的,但基本上我正在寻找一种构建表达式树的方法,该表达式树将返回形状结果,相当于...

select *
from dealerships as d
outer join parts as p on d.dealerid = p.dealerid
    and p.price < 100.00
outer join suppliers as s on p.partid = s.partid
    and s.country = 'brazil'

强调连接条件。

我觉得用esql可能会很简单,但我更倾向于动态构建表达式树。

非常感谢任何建议或指导。


目前还没有,但我仍然很感兴趣并将继续追求... - tim
我也在寻找同样的东西。 - Josue Martinez
5个回答

15
这应该会起作用:
using (TestEntities db = new TestEntities())
{
    var query = from d in db.Dealership
                select new
                {
                    Dealer = d,
                    Parts = d.Part.Where
                    (
                        p => p.Price < 100.0 
                             && p.Supplier.Country == "Brazil"
                    ),
                    Suppliers = d.Part.Select(p => p.Supplier)
                };

    var dealers = query.ToArray().Select(o => o.Dealer);
    foreach (var dealer in dealers)
    {
        Console.WriteLine(dealer.Name);
        foreach (var part in dealer.Part)
        {
            Console.WriteLine("  " + part.PartId + ", " + part.Price);
            Console.WriteLine
                (
                "  " 
                + part.Supplier.Name 
                + ", " 
                + part.Supplier.Country
                );
        }
    }
}

这段代码将为您提供一个包含过滤零部件列表的经销商列表。每个零件都引用一个供应商。有趣的部分是,您必须按照所示的方式在选择中创建匿名类型。否则,Dealership对象的Part属性将为空。

此外,在从查询中选择经销商之前,您必须执行SQL语句。否则,经销商的Part属性再次为空。这就是我在以下行中放置ToArray()调用的原因:

var dealers = query.ToArray().Select(o => o.Dealer);

但我同意Darren的观点,这可能并不是您的库的用户所期望的。


2
关于这个问题,我尝试了一下,最初并没有成功。最终我发现是因为我开启了懒加载导致它无法正常工作。一旦关闭了懒加载,它就像魔术般地正常工作了。感谢Jakob! - msmucker0527
6
需要返回匿名类型而不是原始类型和实际引用。这种方法“确实有效”,但让我想要一种解决方案,可以返回原始对象并将实际集合对象过滤为我真正想要返回的内容。匿名返回类型虽然可行,但不是真正期望的解决方案。 - VulgarBinary
@VulgarBinary,你可以通过以下方式返回一个已输入的列表: var dealers = query.ToArray().Select(o => o.Dealer).ToList(); 它非常好用! - Akira Yamamoto
我更喜欢这种方式,这样我就不会创建不必要的数组:query.Load(); var dealers = query.Select(o => o.Dealer).ToList(); - Akira Yamamoto

7
您确定这是您想要的吗?我问这个问题的唯一原因是,一旦您在“经销商”的“零件”上添加过滤器,您的结果就不再是经销商。您正在处理的是一些特殊对象,它们在很大程度上与经销商非常接近(具有相同的属性),但是“零件”属性的含义不同。它不再是经销商和零件之间的关系,而是一种经过筛选的关系。
换句话说,如果我从您的结果中提取一个经销商并将其传递给我编写的方法,然后在我的方法中调用:
var count = dealership.Parts.Count();

我希望得到来自巴西的零件,而不是经过筛选的零件,价格低于100美元。
如果您不使用经销商对象传递筛选数据,那么它变得非常简单。只需像这样:
    var query = from d in dealerships
               select new { DealershipName = d.Name, 
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) };

如果我只需要像你所要求的那样获取筛选后的集合,我可能会使用我上面提到的技术,然后使用类似Automapper的工具将匿名类中的筛选结果复制到真正的类中。这并不是非常优雅,但应该可以解决问题。
希望这能帮到你!这是一个有趣的问题。

谢谢Darren就对象身份而言,我同意!我也担心这正是为什么EF可能不支持的原因。我真正想做的是解析查询字符串,例如:http://carpartsdb.com/dealerships/parts(price < 100)/suppliers(s.country == brazil)将其转换为查询表达式并在EF中进行检查,再加上自定义的XML序列化策略,就可以得到一个RestQL引擎(RESTful查询语言)。我对EF并不太依赖,但希望能够拥有模型抽象和表达式树,并且不希望与MS堆栈相去甚远! - tim
抱歉,达伦,请在后续回答中查看此文本,那里会更易读! - tim

2
我知道这可以用一个单一的Include来实现。虽然没有测试过两个Includes,但是值得尝试:
dealerships
    .Include( d => d.parts)
    .Include( d => d.parts.suppliers)
    .Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil"))

1

我有没有漏掉什么,或者你只是在寻找Any关键字?

var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) || 
                              d.parts.suppliers.Any(s => s.country == "brazil"));

2
我不这么认为,据我理解,这将返回满足条件的零件和供应商的经销商,而不是返回所有经销商、所有低于100的零件以及巴西的所有供应商。我意识到这应该是一个外连接。我想我试图修剪结果树,而不是基于树下的值过滤顶级节点。 - tim

1

是的,那就是我想做的事情。我认为下一个Data Services版本将有可能执行LINQ to REST查询,这将非常棒。与此同时,我只能切换到加载反向关联和包含相关实体,这会导致它们被多次加载,但理论上只需在第一个Include中加载一次,就像这段代码一样:

return this.Context.SearchHistories.Include("Handle")
    .Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);

之前我尝试加载与逻辑匹配的所有Handle的searchHistories,但不知道如何使用您发布的Include逻辑。因此,在此期间,我认为反向查找将是一个不那么糟糕的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接