【问题标题】:Combine LINQ queries组合 LINQ 查询
【发布时间】:2012-03-21 12:33:08
【问题描述】:

我正在为一组文件结果做一个小查询。

public class f_results
    {
        public String name { get; set; }
        public DateTime cdate { get; set; }
        public DateTime mdate { get; set; }
        public DateTime adate { get; set; }
        public Int64 size { get; set; }
    }

我有一个屏幕,用户可以在上面选择他们想要的内容。目前我正在通过过滤系统:

    foundfiles = new BindingList<f_results>(totalresults.Find(fname.Text,true));
    if (fsize.Text.Trim() != "")
    {
        try
        {
            Int64 sz = Int64.Parse(fsize.Text);
            List<f_results> y = (from p in foundfiles where p.size >= sz orderby p.size descending select p  ).ToList();
            foundfiles = new BindingList<f_results>(y);
        }
        catch
        { }
    }
    if (adate.Text.Trim() != "")
    {
        try
        {
            List<f_results> y;
            DateTime test = DateTime.Parse(adate.Text);
            if ((adateop.Text) == ">")
            {
                y = (from p in foundfiles where p.adate >= test select p).ToList();
            }
            else
                y = (from p in foundfiles where p.adate <= test select p).ToList();
            foundfiles = new BindingList<f_results>(y);
        }
        catch
        { }
    }

    if (mdate.Text.Trim() != "")
    {
        try
        {
            List<f_results> y;
            DateTime test = DateTime.Parse(mdate.Text);
            if ((mdateop.Text) == ">")
            {
                y = (from p in foundfiles where p.mdate >= test select p).ToList();
            }
            else
                y = (from p in foundfiles where p.mdate <= test select p).ToList();
            foundfiles = new BindingList<f_results>(y);
        }
        catch
        { }
    }

    if (cdate.Text.Trim() != "")
    {
        try
        {
            List<f_results> y;
            DateTime test = DateTime.Parse(cdate.Text);
            if ((cdateop.Text) == ">")
            {
                y = (from p in foundfiles where p.cdate >= test select p).ToList();
            }
            else
                y = (from p in foundfiles where p.cdate <= test select p).ToList();
            foundfiles = new BindingList<f_results>(y);
        }
        catch
        { }
    }

最后,我得到了我想要的结果,但我希望处理大约 72 TB 的文件数据,所以我的列表中有很多文件和目录(totalresults 是一个类型的结果,其中包含文件 (f_results) 和目录 (results) 的列表。Find 然后向下迭代并返回与给定正则表达式匹配的大量 f_results 列表。

有没有办法让我的 LINQ 查询一次查询?鉴于并非所有选项都可能使用,例如他们可能只想要文件 > x,或者自.. 或.. 等之后不再使用。

我确实考虑过为测试制作标志,等等,因为它是最重要的测试部分。这是更好的方法,还是有更好的方法?还是说洗完没关系?

【问题讨论】:

  • 要加速大型输入处理,您可以使用Parallel LINQ (PLINQ)?
  • 也许,你有一个我的问题的示例答案(而我试图用棍子戳它,看看我是否可以用其他方式做到这一点?)-顺便说一句,顺序 linq 的示例显示 msdn.microsoft.com/en-us/library/dd460680.aspx 和它并没有真正涵盖这个问题的实际顺序 linq 查询。
  • 我又读了一次问题,所以您希望能够根据指定为字符串的过滤器参数动态构建单个 LINQ 查询吗?或者你的主要目标是什么?

标签: c# linq


【解决方案1】:

您可以预先生成过滤器,然后一次全部应用它们 - 您只需迭代一次初始枚举,如下所示(缩短):

IEnumerable<f_results> foundfiles = new List<f_results>();
var filters = new List<Func<f_results, bool>>();

if (fsize.Text.Trim() != "")
{
    long sz = long.Parse(fsize.Text);
    filters.Add(x => x.size >= sz);
}

if (adate.Text.Trim() != "")
{
    DateTime test = DateTime.Parse(adate.Text);
    filters.Add(x => x.adate >= test);
}

foreach (var filter in filters)
{
    var filterToApply = filter;
    foundfiles = foundfiles.Where(filterToApply);
}
finalResults = new BindingList<f_results>(foundfiles);

更重要的是,在您处理完所有过滤器之前不要调用ToList(),否则您会一遍又一遍地遍历完整的结果列表。

【讨论】:

  • 我喜欢这个样子! (见我将在几秒钟内添加的评论)
  • 那很好。我会将 foreach 循环更改为“foundfiles = foundfiles.Where(file => filters.All(filter => filter(file)))”。
  • 谢谢 BrokenGlass,我很喜欢你的解决方案。我确实找到了一种将其作为单个查询的方法-以防万一有人关心,因此发布了我自己的答案(见下文),但是,我将对此进行调查。我很喜欢。
  • OK 已将其插入,我遇到的唯一问题是 BindingList(foundfiles) 不会接受最后没有 .ToList() 的 foundfiles 类型。我错过了什么?
  • 没错——BindingList 在它的构造函数中只接受了一个IList&lt;T&gt;,所以你必须这样做——当时没查到
【解决方案2】:

好的 - 我想半回答我自己的问题..

我可以合并到一个查询中,以下效果非常好..理想吗?可能不会!

我现在要看看 BrokenGlass 的建议,它看起来又漂亮又整洁!

   Boolean flag_size = false;
    Boolean flag_adate = false;
    Boolean flag_cdate = false;
    Boolean flag_mdate = false;
    Int64 sz=0;
    DateTime adatetest=DateTime.Now;
    DateTime cdatetest = DateTime.Now;
    DateTime mdatetest = DateTime.Now;
    String mop = mdateop.Text;
    String aop = adateop.Text;
    String cop = cdateop.Text;

    if (fsize.Text.Trim() != "")
    {
        try
        {
            sz = Int64.Parse(fsize.Text);
            flag_size = true;
        }
        catch { }
    }

    if (adate.Text.Trim() != "")
    {
        try
        {
            adatetest = DateTime.Parse(adate.Text);
            flag_adate = true;
        }
        catch
        { }
    }
    if (cdate.Text.Trim() != "")
    {
        try
        {
            cdatetest = DateTime.Parse(cdate.Text);
            flag_cdate = true;
        }
        catch
        { }
    }
    if (mdate.Text.Trim() != "")
    {
        try
        {
            mdatetest = DateTime.Parse(mdate.Text);
            flag_mdate = true;
        }
        catch
        { }
    }


    foundfiles = new BindingList<f_results>(totalresults.Find(fname.Text, true));


            List<f_results> y = (from p in foundfiles.AsParallel() 
                                 where  (!flag_size || (flag_size && p.size >= sz)) &&
                                        (!flag_mdate || (flag_mdate && mop == ">" && p.mdate >= mdatetest) || (flag_mdate && mop == "< " && p.mdate >= mdatetest)) &&
                                        (!flag_adate || (flag_adate && aop == ">" && p.adate >= adatetest) || (flag_adate && aop == "< " && p.adate >= adatetest)) &&
                                        (!flag_cdate || (flag_cdate && cop == ">" && p.cdate >= cdatetest) || (flag_cdate && cop == "< " && p.cdate >= cdatetest))
                                 orderby p.size descending 
                                 select p).ToList();

            foundfiles = new BindingList<f_results>(y);

【讨论】:

    【解决方案3】:

    至少我建议在任何地方删除 .ToList() 调用。由于 LINQ 有一个延迟调用,它会迭代一次,即使你有:

    var foundfiles = from p in foundfiles where p.size >= sz select p ;
    foundfiles = from p in foundfiles where p.mdate >= test select p
    

    更新(在这种情况下 order by 应该放在所有过滤器之后)

    但是如果你写:

    var foundfiles = (from p in foundfiles where p.size >= sz orderby p.size descending select p).ToList() ;
    foundfiles = (from p in foundfiles where p.mdate >= test select p).ToList();
    

    它将迭代两次 - 这可能是一个严重的性能问题。

    但我认为如果您将此代码作为单个查询进行,代码看起来不会简单得多。

    另外,为什么要捕获所有异常?你不应该那样做。

    【讨论】:

    • tolist 最初存在的原因是因为它可能不会进行其他测试。一切都是因为它的工作正在进行中,现在,如果它搞砸了,我希望它继续并尝试其余的 :) 稍后会整理。
    • 你最后可能只有一个 ToList() :)
    猜你喜欢
    • 2011-05-05
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    • 1970-01-01
    • 2016-05-24
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    相关资源
    最近更新 更多