Проблемы с методом расширения ForEach через IEnumerable ‹Request.Files›

Выполняя базовую проверку коллекции ASP.Net (4.0) Request.Files (загрузка), я решил попробовать ее с LINQ.

Коллекция IEnumerable<T> и поэтому не предлагает ForEach. По глупости я решил создать метод расширения, который бы справился с этой задачей. Извините, что не очень удачно ...

При запуске метода расширения (см. Ниже) возникает следующая ошибка:

Невозможно привести объект типа System.String к типу System.Web.HttpPostedFile

Я явно чего-то не понимаю, но я не могу понять, что это такое, поэтому, рискуя выглядеть идиотом (не в первый раз), вот код в трех частях вместе с обещанием благодарности за любая помощь.

Во-первых, метод расширения с параметром 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 попадает в «элемент» в «источнике».

Вот вызывающий код (переменные 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));

И, наконец, цель действия, сохранение файла загрузки - мы, кажется, даже не доберемся до этого, но на всякий случай вот он:

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

Я признаю, что некоторые из вышеперечисленных - это совокупность «найденных кусочков», так что я, вероятно, получаю то, что заслуживаю, но если кто-то хорошо понимает это (или, по крайней мере, прошел через это), возможно, я смогу чему-то научиться.

Спасибо за любой.


person Serexx    schedule 11.03.2011    source источник
comment
хорошо, я пробовал это: Request.Files.Cast<HttpPostedFile>().Select(file => new HttpPostedFileWrapper(file)).Where( file => file.ContentLength > 0 && file.ContentLength <= MaxFileSize && file.FileName.Length > 0).ToList<HttpPostedFileWrapper>().ForEach(file=>file.SaveUpload(attachPath,MaxFileTries) ); с тем же результатом   -  person Serexx    schedule 11.03.2011
comment
Зачем вам T в ForEach<T>?   -  person Heinzi    schedule 11.03.2011


Ответы (3)


Почему бы тебе просто не позвонить ToList().ForEach() на исходный IEnumerable<T>.

person Midhat    schedule 11.03.2011
comment
lol, и мне было так весело .... durr ... да, это, вероятно, решит исходную проблему, но я все еще хотел бы выяснить, как это будет сделано ... это превратилось в проблему ;-) - person Serexx; 11.03.2011
comment
k - Я понимаю, о чем вы. Я также читал здесь: блоги. msdn.com/b/ericlippert/archive/2009/05/18/ - так что я не единственный, кто пробует это, и ведутся оживленные дискуссии о том, стоит ли это того. В конце концов, простые расширения внутри обычного цикла ForEach кажутся более ясными, но если это нужно сделать, то универсальное расширение IEnumerable (см. Первый сегмент кода Абатищева ниже) кажется ответом. - person Serexx; 11.03.2011

Я думаю это то что ты хочешь

Ваш класс, расширяющий 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
  });
person Shiv Kumar    schedule 11.03.2011
comment
Спасибо! Я думаю, что понимаю это, но в указанном вами использовании Request.Files.PostedFiles () не будет выполняться для каждого «файла» до того, как будет проверена длина содержимого и имя? Если да, то методы расширения не могут выполнить сохранение .. или я что-то упустил? - person Serexx; 11.03.2011
comment
@Serexx, хорошо, думаю, я понял, что ты пытаешься сделать. Подождите, я отредактирую свой ответ новым кодом. - person Shiv Kumar; 11.03.2011
comment
@Shiv @Serexx Как воспроизвести сценарий, когда Request.Files содержит более 1 элемента? - person abatishchev; 11.03.2011
comment
@abatishchev, боюсь, я не понимаю твой вопрос - person Shiv Kumar; 11.03.2011
comment
Когда я использовал <asp:FileUpload>, он поддерживает только один файл для загрузки, поэтому Request.Files содержит только 1 элемент - выбранный файл. - person abatishchev; 11.03.2011
comment
@abatishchev, вам нужно использовать несколько элементов управления, чтобы разрешить загрузку нескольких файлов - person Shiv Kumar; 11.03.2011

Сначала напишите общий метод расширения:

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<string>, а не в IEnumerable<HttpPostedFile>:

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

но на самом деле System.Web.UI.WebControls.FileUpload.PostedFile это HttpPostedFile:

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));
person abatishchev    schedule 11.03.2011
comment
Хорошо, теперь универсальное расширение имеет для меня больше смысла, но у HttpPostedFile есть свойства, которые мне нужны в моей делегированной функции. Коллекция - это коллекция Request.Files. На самом деле я читал здесь: блоги .msdn.com / b / ericlippert / archive / 2009/05/18 / выглядит так, как будто ваше общее расширение является ответом, но оно прикреплено к коллекции. - person Serexx; 11.03.2011
comment
@Serexx @abatishchev Почему общий метод работает, а другой нет - person Midhat; 11.03.2011
comment
@Midhat: Уточните свой вопрос, пожалуйста - person abatishchev; 11.03.2011
comment
Из вашего комментария я понимаю, что метод, созданный с помощью ForEach ‹T› и использующий T в качестве универсального типа, работал, но он не работал, когда вы указали тип в сигнатуре метода. Почему? - person Midhat; 11.03.2011
comment
@Midhat: метод расширения Serexx неверен, потому что он объявляет общий тип T (ForEach<T>(), но он нигде не используется в теле метода. Таким образом, он даже может быть неуниверсальным и будет выполнять ту же работу. Или может быть общим и универсальным, и будет делать то же самое для любого типа. - person abatishchev; 11.03.2011
comment
@Serexx: Я обновил свой пост для другого, также полезного метода расширения. - person abatishchev; 11.03.2011