简体   繁体   English

有条件的包含在linq中的实体?

[英]conditional include in linq to entities?

I felt like the following should be possible I'm just not sure what approach to take. 我觉得以下应该是可能的我只是不确定采取什么方法。

What I'd like to do is use the include method to shape my results, ie define how far along the object graph to traverse. 我想要做的是使用include方法来塑造我的结果,即定义沿着对象图遍历的距离。 but... I'd like that traversal to be conditional. 但是......我希望这种遍历是有条件的。

something like...

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

I understand that this is not valid linq, in fact, that it is horribly wrong, but essentially I'm looking for some way to build an expression tree that will return shaped results, equivalent to... 我知道这不是有效的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'

with an emphasis on the join conditions. 重点是加入条件。

I feel like this would be fairly straight forward with esql but my preference would be to build expression trees on the fly. 我觉得这对esql来说是相当直接的,但 我最喜欢的是动态构建表达式树。

as always, grateful for any advice or guidance 一如既往,感谢任何建议或指导

This should do the trick: 这应该做的伎俩:

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
                );
        }
    }
}

This code will give you a list of Dealerships each containing a filtered list of parts. 此代码将为您提供经销商列表,每个经销商都包含已过滤的部件列表。 Each part references a Supplier. 每个部分都引用了供应商。 The interesting part is that you have to create the anonymous types in the select in the way shown. 有趣的是,您必须以所示方式在select中创建匿名类型。 Otherwise the Part property of the Dealership objects will be empty. 否则,经销商对象的Part属性将为空。

Also, you have to execute the SQL statement before selecting the dealers from the query. 此外,您必须在从查询中选择经销商之前执行SQL语句。 Otherwise the Part property of the dealers will again be empty. 否则,经销商的Part属性将再次为空。 That is why I put the ToArray() call in the following line: 这就是我将ToArray()调用放在以下行中的原因:

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

But I agree with Darren that this may not be what the users of your library are expecting. 但我同意Darren的观点,这可能不是您图书馆用户所期望的。

Are you sure this is what you want? 你确定这是你想要的吗? The only reason I ask is, once you add the filter on Parts off of Dealerships, your results are no longer Dealerships. 我问的唯一原因是,一旦您在经销商的零件上添加过滤器,您的结果就不再是经销商。 You're dealing in special objects that are, for the most part, very close to Dealerships (with the same properties), but the meaning of the "Parts" property is different. 您处理的特殊对象在很大程度上非常接近经销商(具有相同的属性),但“部件”属性的含义是不同的。 Instead of being a relationship between Dealerships and Parts, it's a filtered relationship. 它不是经销商和零件之间的关系,而是过滤关系。

Or to put it another way, if I pull a dealership out of your results and passed to a method I wrote, and then in my method I call: 换句话说,如果我从你的结果中抽出经销商并传递给我写的方法,然后在我的方法中我打电话:

var count = dealership.Parts.Count();

I'm expecting to get the parts, not the filtered parts from Brazil where the price is less than $100. 我期待得到零件,而不是价格低于100美元的巴西过滤零件。

If you don't use the dealership object to pass the filtered data, it becomes very easy. 如果您不使用经销商对象传递过滤后的数据,则变得非常容易。 It becomes as simple as: 它变得如此简单:

    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")) };

If I just had to get the filtered sets like you asked, I'd probably use the technique I mentioned above, and then use a tool like Automapper to copy the filtered results from my anonymous class to the real class. 如果我只是像你要求的那样得到过滤后的集合,我可能会使用上面提到的技术,然后使用像Automapper这样的工具将过滤后的结果从我的匿名类复制到真正的类。 It's not incredibly elegant, but it should work. 它不是非常优雅,但它应该工作。

I hope that helps! 我希望有所帮助! It was an interesting problem. 这是一个有趣的问题。

Am I missing something, or aren't you just looking for the Any keyword? 我错过了什么,或者你不只是在寻找Any关键字?

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

Yes that's what I wanted to do I think the next realease of Data Services will have the possiblity to do just that LINQ to REST queries that would be great in the mean time I just switched to load the inverse and Include the related entity that will be loaded multiple times but in theory it just have to load once in the first Include like in this code 是的,这就是我想要做的事情我认为数据服务的下一个版本将有可能做到LINQ to REST查询,这个问题在我刚刚切换到加载逆向并包含相关实体的同时会很好。加载多次,但理论上它只需要在第一个包含中加载一次,就像在此代码中一样

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

before I tried to load for any Handle the searchHistories that matched the logic but don't know how using the Include logic you posted so in the mean time I think a reverse lookup would be a not so dirty solution 在我尝试加载任何句柄之前,搜索与逻辑匹配的历史但不知道如何使用你发布的包含逻辑,所以我认为反向查找将是一个不那么肮脏的解决方案

I know this can work with one single Include. 我知道这可以使用一个包含。 Never test with two includes, but worth the try: 永远不要测试两个包含,但值得尝试:

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"))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM