【问题标题】:Problems with ForEach extension method over IEnumerable<Request.Files>IEnumerable<Request.Files> 上的 ForEach 扩展方法的问题
【发布时间】:2011-03-11 06:02:25
【问题描述】:

在对 ASP.Net (4.0) Request.Files(上传)集合进行一些基本验证时,我决定尝试使用 LINQ。

集合是IEnumerable&lt;T&gt;,因此不提供 ForEach。愚蠢地我决定建立一个可以完成这项工作的扩展方法。很抱歉说没有那么成功...

运行扩展方法(如下)引发下一个错误:

无法将“System.String”类型的对象转换为“System.Web.HttpPostedFile”类型

显然有些东西我没有得到,但我看不到它是什么,所以冒着看起来像个白痴的风险(不会是第一次)这里是 3 块代码,以及一个承诺感谢任何帮助。

一、带有Action参数的扩展方法:

//Extend ForEach to IEnumerated Files
public static IEnumerable<HttpPostedFileWrapper> ForEach<T>(this IEnumerable<HttpPostedFileWrapper> source, Action<HttpPostedFileWrapper> action)
{
   //breaks on first 'item' init
   foreach (HttpPostedFileWrapper item in source)
        action(item);
    return source;
}

当内部 foreach 循环命中 'source' 中的 'item' 时会发生错误。

这是调用代码(变量 MaxFileTries 和 attachPath 之前已正确设置):

var files = Request.Files.Cast<HttpPostedFile>()
    .Select(file => new HttpPostedFileWrapper(file))
    .Where(file => file.ContentLength > 0
        && file.ContentLength <= MaxFileSize
        && file.FileName.Length > 0)
    .ForEach<HttpPostedFileWrapper>(f => f.SaveUpload(attachPath, MaxFileTries));

最后,Action 目标,保存上传文件 - 我们似乎从来没有到过这里,但以防万一,这里是:

public static HttpPostedFileWrapper SaveUpload(this HttpPostedFileWrapper f, string attachPath, int MaxFileTries)
{
    // we can only upload the same file MaxTries times in one session
    int tries = 0;
    string saveName = f.FileName.Substring(f.FileName.LastIndexOf("\\") + 1);   //strip any local
    string path = attachPath + saveName;
    while (File.Exists(path) && tries <= MaxFileTries)
    {
        tries++;
        path = attachPath + " (" + tries.ToString() + ")" + saveName;
    }
    if (tries <= MaxFileTries)
    {
        if (!Directory.Exists(attachPath)) Directory.CreateDirectory(attachPath);
        f.SaveAs(path);
    }
    return f;
}

我承认上面的一些内容是“发现的一些东西”的拼凑,所以我可能会得到我应得的,但如果有人对此有很好的理解(或至少经历过),也许我可以学习一些东西。

谢谢。

【问题讨论】:

  • 好的,我试过这个:Request.Files.Cast&lt;HttpPostedFile&gt;().Select(file =&gt; new HttpPostedFileWrapper(file)).Where( file =&gt; file.ContentLength &gt; 0 &amp;&amp; file.ContentLength &lt;= MaxFileSize &amp;&amp; file.FileName.Length &gt; 0).ToList&lt;HttpPostedFileWrapper&gt;().ForEach(file=&gt;file.SaveUpload(attachPath,MaxFileTries) ); 结果相同
  • 为什么需要ForEach&lt;T&gt; 中的T

标签: c# linq foreach extension-methods ienumerable


【解决方案1】:

你为什么不直接在原来的IEnumerable&lt;T&gt;上调用ToList().ForEach()

【讨论】:

  • 大声笑,我玩得很开心.... durr.. 是的,这可能会解决最初的问题,但我仍然想弄清楚它是如何完成的......它的变成了挑战;-)
  • k - 我明白你的意思。不过我也在这里读到:blogs.msdn.com/b/ericlippert/archive/2009/05/18/… - 所以,我不是唯一一个尝试这个的人,关于它是否值得的辩论似乎很激烈。归根结底,普通 ForEach 循环中的简单扩展似乎更清晰,但如果要这样做,那么通用 IEnumerable 扩展(请参阅下面的 abatishchev 的第一个代码段)似乎是答案。
【解决方案2】:

我想这就是你想要的

你的扩展 HttpFileCollection 的类应该是

  public static class HttpPostedFileExtension
  {
    //Extend ForEach to IEnumerated Files
    public static void ProcessPostedFiles(this HttpFileCollection source, Func<HttpPostedFile, bool> predicate, Action<HttpPostedFile> action)
    {
      foreach (var item in source.AllKeys)
      {
        var httpPostedFile = source[item];
        if (predicate(httpPostedFile))
          action(httpPostedFile);
      }
    }  
  }

然后你可以像这样使用它:

  Request.Files.ProcessPostedFiles(
  postedFile =>
  {
    return (postedFile.ContentLength > 0 && postedFile.FileName.Length > 0);      
  },
  pFile =>
  {
    //Do something with pFile which is an Instance of HttpPosteFile
  });

【讨论】:

  • 谢谢!我想我理解这一点,但是在您给出的用法中,在检查内容长度之前不会为每个“文件”执行 Request.Files.PostedFiles() 名称?如果是这样,那么扩展方法无法真正执行保存.. 还是我遗漏了什么?
  • @Serexx,好的,我想我明白了你想要做什么。等一下,我会用新代码编辑我的答案。
  • @Shiv @Serexx 如何重现Request.Files 包含超过 1 个项目的场景?
  • @abatishchev,恐怕我不明白你的问题
  • 当我使用&lt;asp:FileUpload&gt;时,它只支持一个文件上传,所以Request.Files只包含一个项目——选择的文件。
【解决方案3】:

首先,写一个常用的扩展方法:

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
   foreach (T item in source)
        action(item);
    return source; // or void, without return
}

那么,Request.Files 可以转换为IEnumerable&lt;string&gt;,而不是IEnumerable&lt;HttpPostedFile&gt;

IEnumerable<string> requestFiles = this.Request.Files.Cast<string>();

但实际上System.Web.UI.WebControls.FileUpload.PostedFileHttpPostedFile

HttpPostedFile file = fileUpload.PostedFile;
HttpPostedFileWrapper wrapper = new HttpPostedFileWrapper(file);

但它是单一的,而不是集合。你的收藏是从哪里得到的?


另一种扩展方法:

public static IEnumerable<HttpPostedFile> ToEnumerable(this HttpFileCollection collection)
{
    foreach (var item in collection.AllKeys)
    {
        yield return collection[item];
    }
}

用法:

IEnumerable<HttpPostedFile> files = this.Request.Files.ToEnumerable();
IEnumerable<HttpPostedFileWrapper> wrappers = files.Select(f => new HttpPostedFileWrapper(f));

【讨论】:

  • 好的,通用扩展现在对我来说更有意义,但是 HttpPostedFile 在我的委托函数中具有我需要的属性。 Collection 是 Request.Files 集合。实际上我在这里读过:blogs.msdn.com/b/ericlippert/archive/2009/05/18/… 看起来你的通用扩展是答案,但附加到集合中。
  • @Serexx @abatishchev 为什么泛型方法有效,而其他方法无效
  • @Midhat:请详细说明您的问题
  • 从您的评论中,我了解到使用 ForEach 创建并使用 T 作为泛型类型的方法有效,但是当您在方法签名中指定类型时它不起作用。为什么?
  • @Midhat:Serexx 的扩展方法不正确,因为它声明了泛型类型TForEach&lt;T&gt;(),但它没有在方法体中使用。所以它甚至可以是非泛型的,并且可以同样的工作。或者可以是通用的和通用的,并且对任何类型都可以做同样的工作。对吧?
猜你喜欢
  • 1970-01-01
  • 2012-01-16
  • 2018-09-06
  • 1970-01-01
  • 2019-03-17
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 2011-01-01
相关资源
最近更新 更多