здесь есть асинхронный контроллер? - PullRequest
0 голосов
/ 04 января 2012

У меня есть система, в которой пользователи могут загружать иногда большие (100-200 МБ) файлы из приложения MVC3. Я хотел бы не блокировать пользовательский интерфейс во время загрузки файла, и после некоторых исследований выяснилось, что новый AsyncController может позволить мне делать то, что я пытаюсь сделать. Проблема в том, что каждый пример, который я видел, на самом деле не делает одно и то же, поэтому я, похоже, упускаю одну важную деталь После большого возни и возни вот мой текущий код:

public void CreateAsync(int CompanyId, FormCollection fc)
    {
        UserProfile up = new UserRepository().GetUserProfile(User.Identity.Name);
        int companyId = CompanyId;
        // make sure we got a file..
        if (Request.Files.Count < 1)
        {
            RedirectToAction("Create");
        }

        HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
        if (hpf.ContentLength > 0)
        {
            AsyncManager.OutstandingOperations.Increment();

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (o, e) =>
                {
                    string fileName = hpf.FileName;
                    AsyncManager.Parameters["recipientId"] = up.id;
                    AsyncManager.Parameters["fileName"] = fileName;
                };
            worker.RunWorkerCompleted += (o, e) => { AsyncManager.OutstandingOperations.Decrement(); };
            worker.RunWorkerAsync();

        }

        RedirectToAction("Uploading");
    }

public void CreateCompleted(int recipientId, string fileName)
    {
        SystemMessage msg = new SystemMessage();
        msg.IsRead = false;
        msg.Message = "Your file " + fileName + " has finished uploading.";
        msg.MessageTypeId = 1;
        msg.RecipientId = recipientId;
        msg.SendDate = DateTime.Now;
        SystemMessageRepository.AddMessage(msg);
    }

    public ActionResult Uploading()
    {
        return View();
    }

Теперь идея состоит в том, чтобы пользователь отправил файл, вызвал фоновый процесс, который будет делать кучу вещей (для целей тестирования пока просто извлекает имя файла), одновременно направляя их в представление Uploading, которое просто говорит msgstr "ваш файл загружается ... продолжайте, и мы сообщим вам, когда он будет готов". Метод CreateCompleted обрабатывает это уведомление, вставляя сообщение в очередь сообщений пользователя.

Так что проблема в том, что я никогда не получаю представление Uploading. Вместо этого я получаю пустое представление Create. Я не могу понять, почему. Это потому, что вызывается метод CreateCompleted, который показывает представление Create? Зачем это делать, если он возвращается пустым? Я просто хочу, чтобы он выполнялся в фоновом режиме, вставлял сообщение и останавливался.

Так что же это правильный подход для ВСЕХ? Моя полная причина в том, чтобы сделать это с некоторыми скоростями сети, загрузка файла может занять 30 минут, и в его текущей версии он блокирует все приложение до его завершения. Я бы предпочел не использовать что-то вроде всплывающего окна, если я могу избежать этого, так как это приводит к множеству проблем поддержки со скриптами блокировки всплывающих окон и т. Д.

Во всяком случае - у меня нет идей. Предложения? Помогите? Альтернативные методы, которые я мог бы рассмотреть?

Заранее спасибо.

1 Ответ

2 голосов
/ 04 января 2012

Вы все здесь делаете неправильно. Предположим, что ваше действие называется Create.

  1. CreateAsync перехватит запрос и должен быть пустым методом и ничего не возвращает. Если у вас есть атрибуты, вы должны применить их к этому методу.
  2. CreateCompleted - это ваш метод, который вы должны рассматривать как стандартный метод действия контроллера, и вы должны вернуть ActionResult внутри этого метода.

Вот простой пример для вас:

[HttpPost]
public void CreateAsync(int id) { 

    AsyncManager.OutstandingOperations.Increment();

    var task = Task<double>.Factory.StartNew(() => {

        double foo = 0;

        for(var i = 0;i < 1000; i++) { 
            foo += Math.Sqrt(i);
        }

        return foo;

    }).ContinueWith(t => {
        if (!t.IsFaulted) {

            AsyncManager.Parameters["headers1"] = t.Result;
        }
        else if (t.IsFaulted && t.Exception != null) {

            AsyncManager.Parameters["error"] = t.Exception;
        }

        AsyncManager.OutstandingOperations.Decrement();
    });

}

public ActionResult CreateCompleted(double headers1, Exception error) { 

    if(error != null)
        throw error;

    //Do what you need to do here

    return RedirectToAction("Index");
}

Также имейте в виду, что этот метод будет по-прежнему блокировать операцию до завершения операции. Это не асинхронная операция типа «запусти и забудь».

Для получения дополнительной информации, посмотрите:

Использование асинхронного контроллера в ASP.NET MVC

Редактировать

То, что вы хотите здесь, это что-то вроде приведенного ниже кода. Забудьте обо всех вещах AsyncController, и это ваш метод создания действия post:

    [HttpPost]
    public ActionResult About() {

        Task.Factory.StartNew(() => {

            System.Threading.Thread.Sleep(10000);

            if (!System.IO.Directory.Exists(Server.MapPath("~/FooBar")))
                System.IO.Directory.CreateDirectory(Server.MapPath("~/FooBar"));

            System.IO.File.Create(Server.MapPath("~/FooBar/foo.txt"));

        });

        return RedirectToAction("Index");
    }

Обратите внимание, что я ждал там 10 секунд, чтобы это стало реальностью. После того, как вы сделаете сообщение, вы увидите, что оно вернется немедленно, не дожидаясь. Затем откройте корневую папку вашего приложения и посмотрите. Вы заметите, что папка и файл будут созданы через 10 секунд.

Но (большой) здесь нет обработки исключений, логика, как уведомить пользователя и т. Д.

На вашем месте я бы посмотрел на другой подход или заставил бы пользователя страдать и ждать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...