【问题标题】:LINQ to Objects - Philosophy of use?LINQ to Objects - 使用哲学?
【发布时间】:2009-03-24 07:23:49
【问题描述】:

我开始探索 LINQ to Objects,但想知道应用程序设计如何最好地适应 LINQ to Objects。

举个例子......一个应用程序只显示女性员工,并且数据库中的员工表包含男性和女性员工的数据。

默认情况下,应用程序会构建一条 SQL 语句,仅从数据库中检索女性员工,并将她们放入一个对象(列表)中,然后在应用程序中传递该对象以进行显示等。

为了最大限度地利用 LINQ 到对象,我假设您首先必须获取员工的所有数据,然后应用 LINQ 过滤对象以仅显示女性员工。好处是您可以继续查询员工而无需再次接触数据库。

这是一个基本示例,但我要说明的是,收益和高效执行时间之间的界限在哪里交叉。

如果员工表有 100,000 条记录怎么办?

我可以看到 LINQ 的好处 .... 但在我的脑海中,我相信您首先需要将所有数据放入一个对象中,然后您可以使用 LINQ 进行查询。

当我每次需要不同的数据子集时都可以再次查询数据库时,获取所有数据的成本可能太高,不值得。

想法?

【问题讨论】:

标签: .net linq object


【解决方案1】:

如果您只需要数据的一个子集,请只获取该子集。我认为在您的示例中获取所有员工数据没有意义。

但是,这并不意味着 LINQ to Objects 很少有用 - 通常当您已经获得一些数据时(很可能不是来自数据库 - 例如,我发现它对反射非常有用)您想用几种方法切片和切块。 LINQ to Objects 是一个非常强大的工具。

就个人而言,我发现 LINQ to Objects 是最引人注目的 LINQ 提供程序。很容易预测它将做什么(因为不涉及翻译),并且它在应用程序的每一层都很有用。我在编写 Java 时非常想念它——几乎任何时候我对集合做任何有趣的事情时,LINQ to Objects 都会让它变得更容易。

【讨论】:

    【解决方案2】:

    当您处理来自数据库的数据时,您是否应该依赖 LINQ-to-Objects 或在数据库中进行查询开始变得模糊。我认为一般来说,如果您有数据库,最好在数据库中进行尽可能多的过滤和排序,并谨慎使用 LINQ to Objects,尤其是在处理大量数据时。

    但是,由于 DAL 代码可能有点笨拙,我发现有时在 DAL 中运行 FindAll 查询会更容易,然后只需使用 LINQ 对对象进行排序和过滤。 (但前提是您的收藏量很小)。

    LINQ-to-Objects 可用于对内存中的集合进行类似数据库的排序和过滤。这可能是您从数据库中提取的需要进一步过滤的集合,但也可以是代码中的任何集合。

    【讨论】:

      【解决方案3】:

      LINQ 不仅仅与数据库有关。

      简而言之,它为您提供了对结构(可以是行、XML、对象的内存列表等)的查询功能(使用 DB 或不使用 DB)。这样,您不必编写代码来手动执行操作,并且更具可读性

      想象一下,必须比较两个列表并尝试使用 C# 代码找到其中的共同元素。使用 SQL 执行此操作将很容易理解,但在 c# 中执行相同操作将需要更多代码(除非您尝试这样做,否则它将无法读取)

      LINQ 提供了语法糖,让您看起来像是在编写 SQL 来查询/排序/聚合事物。大多数开发人员都可以阅读 SQL。

      编辑:假设您拥有希望向用户显示的数据子集。现在,您想要某种过滤/聚合/排序操作,这样您就不必使用数据库来完成所有这些操作,您将如何做?

      如果有东西将我的集合视为某种可查询、可排序、聚合的结构(类似于 SQL 中的表)怎么办?

      【讨论】:

        【解决方案4】:

        您可以使用 LINQ 进行的不仅仅是查询数据库。类似于 LINQ to database entity/XML/etc 从延迟处理中受益的方式;对数组、集合、对象图和几乎任何其他你能想到的内存结构的查询也是如此。我的意思是延迟处理;您可以定义查询(分配给变量),并且在您开始枚举结果之前它实际上不会执行。此外,LINQ 的谓词逻辑到任何可链接的对象;考虑过滤pipelines

        例如,假设您想要创建一个去除标点符号和空格的字符串扩展方法。您可以像这样针对字符串创建查询:

        public static string RemovePunctuation(this string @string)
        {
            if (string.IsNullOrEmpty(@string))
                return @string;
        
            var bytes = (
                from ch in @string
                where !Char.IsPunctuation(ch) && !Char.IsWhiteSpace(ch)
                select Convert.ToByte(ch)
                ).ToArray(); // <- calling to array enumerates the results
        
            return UnicodeEncoding.ASCII.GetString(bytes);
        }
        

        当然还有其他方法可以做同样的事情,但这是一个有趣的 LINQ 对字符串的使用,它执行得很好。

        【讨论】:

          【解决方案5】:

          使用哲学?这是您可以在其他地方应用的一般答案。

          阅读一些工具有用的例子。如果您发现自己缺乏使用该工具的理由,因为您永远不会出现类似的示例,那么请忘记它。它可能不适用于您的域。

          如果您操作的所有数据都在 RDBMS 中,那么您可能根本不需要 Linq to Objects。

          另一方面...您可能将其视为对数据库中的数据进行一些额外操作的一种方式,因此错过了加强代码表现力的机会,而这无关紧要处理数据库。

          示例:您正在读取一个由纯文本行组成的文件。

          var lines = File.ReadAllLines(fileName);
          

          碰巧,lines 现在包含一个字符串数组,但数组支持 IEnumerable,因此我们可以对它们使用 Linq 方法。假设您要删除其中没有任何内容的行:

          var nonBlankLines = lines.Where(line => line.Trim() == string.Empty);
          

          假设您想要引号中的那些字符串(天真的实现 - 需要转义现有引号!):

          var quoted = lines.Where(line => line.Trim() == string.Empty)
                            .Select(line => "\"" + line + "\"");
          

          (我喜欢将连续的操作排列在一起,点法相互对齐。)

          除非我要对这些线条做其他事情,否则我会这样做:

          var quoted = File.ReadAllLines(fileName)
                           .Where(line => line.Trim() == string.Empty)
                           .Select(line => "\"" + line + "\"");
          

          然后假设我希望这一切都变成一个用逗号分隔的字符串,如果我先把它全部变成一个数组,那么字符串中有一个名为 Join 的方法可以做到这一点:

          var quoted = string.Join(", ", 
                                   File.ReadAllLines(fileName)
                                       .Where(line => line.Trim() == string.Empty)
                                       .Select(line => "\"" + line + "\"")
                                       .ToArray());
          

          或者我们可以使用 Linqy 的方式来做:

          var quoted = File.ReadAllLines(fileName)
                           .Where(line => line.Trim() == string.Empty)
                           .Select(line => "\"" + line + "\"")
                           .Aggregate((a, b) => a + ", " + b);
          

          此外,填补一些空白也没什么大不了的,因为在这些空白处你发现没有现有的运算符来满足你的需要(尽管有时事实证明已经有一个)。缺少一个大的与Aggregate 相反,我已将其称为Util.Generate

          IEnumerable<T> Generate<T>(T item, Func<T, T> generator)
          {
              for (; item != null; item = generator(item))
                  yield return item;
          }
          

          当您有一个链表时,这会非常方便,这种链表偶尔会出现在对象模型中。一个例子是Exception.InnerException,它允许异常形成一个链表,最里面的一个在最后。假设我们只想显示来自x 最内层异常的消息:

          MessageBox.Show(Util.Generate(x, i => i.InnerException).Last().Message);
          

          Generate 辅助方法将链表转换为 IEnumerable,允许其他 Linq 方法对其进行处理。只需要给它一个 lambda 来告诉它如何从当前项到达下一项。

          也许这会让您入门,或者您可能需要更多示例,或者您可能从不操作任何非来自 RDBMS 的数据。

          【讨论】:

            【解决方案6】:

            LinqToObjects 并不是要高效地处理非索引集合中的 100,000 个元素。 foreach 循环也不是。

            从哲学上讲,LinqToObjects 和 foreach 在同一个空间中工作。检查代码中的 foreach 循环,看看它们是否更富有表现力地编写为 LinqToObjects 查询。

            【讨论】:

              猜你喜欢
              • 2018-03-03
              • 1970-01-01
              • 2011-10-22
              • 1970-01-01
              • 2019-03-20
              • 2010-10-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多