【问题标题】:"IN" Operator in LinqLinq 中的“IN”运算符
【发布时间】:2010-01-14 17:22:33
【问题描述】:

我正在尝试使用实体框架在 Linq 中转换旧的原始 Sql 查询。

它使用了 IN 操作符和一个项目集合。查询是这样的:

SELECT Members.Name
FROM Members
WHERE Members.ID IN ( SELECT DISTINCT ManufacturerID FROM Products WHERE Active = 1)
ORDER BY Members.Name ASC

由于子查询返回的不是单个字符串而是字符串集合,所以我不能使用String.Contains() 方法。

我想过做类似的事情:

var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);

然后

var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));

但它停在包含说它有无效参数...我不能选择 activeProducts.ManufacturerID 因为显然属性不存在,因为它返回一个 IQueryable...

我在这里要做的是返回至少拥有一种有效产品的成员列表。

有什么提示吗?

[编辑]

这是完整的查询代码...我尝试在第二个表达式中使用 contains,Linq 似乎不喜欢它:

Server Error in '/' Application. LINQ to Entities does not recognize the method 'Boolean Contains[String](System.Linq.IQueryable``1[System.String], System.String)' method, and this method cannot be translated into a store expression.

    var activeProduct =(from product in Master.DataContext.ProductSet
                        where product.Active == true
                           && product.ShowOnWebSite == true
                           && product.AvailableDate <= DateTime.Today
                           && ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
                        select product.ManufacturerID.ToString() );

    var activeArtists = from artist in Master.DataContext.ContactSet
                        where activeProduct.Contains(artist.ID.ToString())
                        select artist;

    NumberOfArtists = activeArtists.Count();

    artistsRepeater.DataSource = activeArtists;
    artistsRepeater.DataBind();

[更多详情] ManufacturerID 显然是一个可为空的 GUID...

由于某种原因,ContactSet 类不包含对产品的任何引用,我想我将不得不进行连接查询,这里没有线索。

【问题讨论】:

    标签: c# linq entity-framework c#-3.0


    【解决方案1】:
    var activeMembers = (
    from member in db.ContactSet
    where activeProducts.Select(x=>x.ID).Contains(member.ID));
    

    【讨论】:

      【解决方案2】:

      试试where activeProducts.Contains(member.ID)
      编辑:你试过没有ToStrings吗?

      【讨论】:

      • 是的,Linq 仍然不喜欢它,我刚刚发现 ManufacturerID 是一个 Guid? (可为空)...如果我执行`select ManufacturerID.Value` 我有这个:LINQ to Entities 无法识别方法'Boolean Contains[Guid](System.Linq.IQueryable`1[System.Guid], System. Guid)' 方法,并且该方法不能翻译成商店表达式。
      • 这和IN操作符一样高效吗?
      • @tofutim:这应该翻译成IN 子句。 (免责声明:我没有检查)
      【解决方案3】:

      您可以在一个查询中完成:

      var q = from member in db.ContactSet
              where member.Products.Any(p => p.IsActive)
              select member;
      

      【讨论】:

      • 我不得不简化语句,但“位置”有更多条件......我编辑了我的帖子以包含完整的代码。
      • 您仍然可以在一个查询中完成。将整个 where 子句移至 Any 调用。
      • 显然没有从 ContactSet 到 ProductSet 的链接(哦,我多么喜欢第 3 方软件修改),我想我将不得不进行连接查询...
      • 即使使用join,您仍然可以在一个查询中完成。
      【解决方案4】:

      【讨论】:

        【解决方案5】:

        这个呢:

        from m in members
        where products.FirstOrDefault(prod => prod.IsActive == 1 && prod.Id == m.Id) != null
        select m;
        

        您可以使用 && 链接 where 子句中所需的任意数量的条件

        灰..

        【讨论】:

          【解决方案6】:
          from m in members
          where products.Any(p => p.Active && p.ManufacturerID == m.ID)
          select m
          

          from m in members
          join p in products on m.ID equals p.ManufacturerID
          where p.Active
          select m
          

          【讨论】:

            【解决方案7】:

            而不是这个:

            var activeMembers = (
            from member in db.ContactSet
            where member.ID.ToString().Contains(activeProducts));
            

            试试这个:

            var activeMembers = (
            from member in db.ContactSet
            where activeProducts.Contains(member.ID));
            

            【讨论】:

            • 试过了,Linq 好像不太喜欢。
            【解决方案8】:

            如果你交换语句(未经测试)怎么办?

            where activeProducts.Contains(member.ID)
            

            【讨论】:

              【解决方案9】:

              这个怎么样...

              var activeProducts = (
              from products in db.ProductSet
              where product.Active == true
              select product.ManufacturerID);
              
              var activeMembers = (
              from member in db.ContactSet
              where activeProducts.Contains(member.ID.ToString()));
              

              【讨论】:

              • 我认为这个改变会让它起作用... var activeProducts = ( from products in db.ProductSet where product.Active == true select product.ManufacturerID); var activeMembers = ( from member in db.ContactSet where activeProducts.Select(x=>x.id).Contains(member.ID.ToString()));
              • product.Active == true 有点多余不是吗?只需product.Active 就足够了。
              • @Askolein 我觉得它更具可读性,可能是个人选择。宁愿将“Active”重命名为“IsActive”,然后删除与真/假的比较。
              【解决方案10】:

              在查询内存中的对象时,帮助程序或扩展方法可以正常工作。但是针对 SQL 数据库,您的 LINQ 代码将被编译成表达式树,进行分析并转换为 SQL 命令。此功能没有自定义扩展方法或.Contains(...) 等其他对象的方法的概念。

              不过,它可以很容易地被 Microsoft 实现到标准的 LINQ-To-SQL 功能中。但只要他们不想要,只要不是开源功能,我们就束手无策。

              您所能做的就是创建自己的针对 SQL 数据库的 QueryProvider。但这会很困难,而且您仅缺少一个 in 功能。

              但是,如果您真的想走那条路,请玩得开心:LINQ: BUILDING AN IQUERYABLE PROVIDER SERIES

              【讨论】:

                【解决方案11】:

                最后我设法编写了一些非常丑陋的代码,但这确实有效! (笑)

                    var activeProduct =(from product in Master.DataContext.ProductSet
                                        where product.Active == true
                                           && product.ShowOnWebSite == true
                                           && product.AvailableDate <= DateTime.Today
                                           && ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
                                        select product.ManufacturerID ).Distinct();
                
                    var artists = from artist in Master.DataContext.ContactSet
                                        select artist;
                
                    List<Evolution.API.Contact> activeArtists = new List<Evolution.API.Contact>();
                
                    foreach (var artist in artists)
                    {
                        foreach(var product in activeProduct)
                        {
                            if (product.HasValue && product.Value == artist.ID)
                                activeArtists.Add(artist);
                        }
                    }
                
                    NumberOfArtists = activeArtists.Count();
                
                    artistsRepeater.DataSource = activeArtists;
                    artistsRepeater.DataBind();
                

                【讨论】:

                • 非常糟糕的解决方案
                【解决方案12】:
                【解决方案13】:
                var q = (from p in db.DOCAuditTrails
                        where p.ActionUser == "MyUserID"
                        && p.ActionTaken == "Not Actioned"
                        && p.ActionDate > DateTime.Parse("2011-09-13")
                          select p.RequisitionId).Distinct();
                
                var DocAuditResults = db.DOCAuditTrails.Where(p 
                  => q.ToArray().Contains(p.RequisitionId));
                

                【讨论】:

                  【解决方案14】:

                  如果不知道确切的映射,就很难判断什么可以做,什么不能做。我会假设不涉及任何铸造。首先,您必须记住,Linq 表达式树中的所有内容都必须在 SQL 中具有等价物。正如其他人所指出的,您的 Linq 语句中有一个 object.ToString()。

                  但是似乎人们忽略了提及的是您有两个 object.ToSting() 用法,都必须删除。

                  我还会创建一个额外的变量来将闭包的捕获类型显式更改为 DataContext(因为 Linq 语句就像一个 lambda,并且延迟评估。它将需要获取整个 Master 变量。之前我说过您的 Linq 中的所有内容都必须在 SQL 中具有等效项。鉴于 Master 不可能存在于 SQL 中,因此 Master 类型没有 DataContext 属性/列/映射。

                  var context = Master.DataContext;
                  var activeProduct = from product in context.ProductSet
                                      where product.Active == true
                                         && product.ShowOnWebSite == true
                                         && product.AvailableDate <= DateTime.Today
                                         && ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
                                      select product.ManufacturerID;
                  
                  var activeArtists = from artist in context.ContactSet
                                      where activeProduct.Contains(artist.ID)
                                      select artist;
                  

                  我希望以上更改对您有用。

                  在许多情况下,Linq to ORM 的问题可以追溯到您的 Linq 表达式捕获非原始(日期时间、整数、字符串等)和非基于 ORM 的类(DataContext/EntityObject 等)。另一个主要问题是使用 ORM 未公开的函数和运算符(可以通过 ORM 将用户定义的函数映射到 .net 函数,但由于索引问题,我不建议这样做)。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-09-22
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多