Асинхронный контроллер T4MVC

Недавно я установил T4MVC с помощью Nuget. До этого у меня была старая версия, которая работала нормально, однако у меня было новое требование, требующее асинхронного контроллера. После создания асинхронного контроллера мой проект не скомпилировался из-за того, что он связан с t4. Поэтому я обновился с помощью Nuget.

Проблема, с которой я столкнулся сейчас, заключается в том, что мой контроллер работал нормально, пока я недавно не обновил t4mvc. После обновления мой код компилируется, но когда я вызываю асинхронное действие, он возвращается через долгое время и возвращает огромное количество неверных данных. Кроме того, что очень важно, если я устанавливаю точки останова в своем контроллере, он больше не попадает в них?! Если я верну свой DocumentController.generated.cs в последнюю рабочую версию, все будет работать нормально.

Ниже мой старый DocumentController.generated.cs, который работает, и новый, который не работает.

Может ли кто-нибудь помочь мне понять, что здесь происходит? Я стал полагаться на T4MVC, потому что он так хорош, однако я действительно не могу избежать этих асинхронных действий.

СТАРЫЙ

// <auto-generated />
// This file was generated by a T4 template.
// Don't change it directly as your change would get overwritten.  Instead, make changes
// to the .tt file (i.e. the T4 template) and save it to regenerate this file.

// Make sure the compiler doesn't complain about missing Xml comments
#pragma warning disable 1591
#region T4MVC

using System;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
using T4MVC;
namespace WebUI.Client.Controllers {
    public partial class DocumentController {
        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        public DocumentController() { }

        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        protected DocumentController(Dummy d) { }

        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        protected RedirectToRouteResult RedirectToAction(ActionResult result) {
            var callInfo = result.GetT4MVCResult();
            return RedirectToRoute(callInfo.RouteValueDictionary);
        }


        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        public DocumentController Actions { get { return MVC.Document; } }
        [GeneratedCode("T4MVC", "2.0")]
        public readonly string Area = "";
        [GeneratedCode("T4MVC", "2.0")]
        public readonly string Name = "Document";

        static readonly ActionNamesClass s_actions = new ActionNamesClass();
        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        public ActionNamesClass ActionNames { get { return s_actions; } }
        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        public class ActionNamesClass {
            public readonly string Index = "Index";
        }


        static readonly ViewNames s_views = new ViewNames();
        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        public ViewNames Views { get { return s_views; } }
        [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
        public class ViewNames {
            public readonly string Index = "~/Views/Document/Index.aspx";
        }
    }

    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public class T4MVC_DocumentController: WebUI.Client.Controllers.DocumentController         

    {
        public T4MVC_DocumentController() : base(Dummy.Instance) { }

        public override System.Web.Mvc.ActionResult Index() {
            var callInfo = new T4MVC_ActionResult(Area, Name, ActionNames.Index);
            return callInfo;
        }

    }
}

#endregion T4MVC
#pragma warning restore 1591

НОВЫЙ

// <auto-generated />
// This file was generated by a T4 template.
// Don't change it directly as your change would get overwritten.  Instead, make changes
// to the .tt file (i.e. the T4 template) and save it to regenerate this file.

// Make sure the compiler doesn't complain about missing Xml comments
#pragma warning disable 1591
#region T4MVC

using System;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
using T4MVC;
namespace WebUI.Client.Controllers {
    public partial class DocumentController {
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public DocumentController() { }

    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    protected DocumentController(Dummy d) { }

    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    protected RedirectToRouteResult RedirectToAction(ActionResult result) {
        var callInfo = result.GetT4MVCResult();
        return RedirectToRoute(callInfo.RouteValueDictionary);
    }

    [NonAction]
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public System.Web.Mvc.JsonResult GetDocumentListCompleted() {
        return new T4MVC_JsonResult(Area, Name, ActionNames.GetDocumentListCompleted);
    }
    [NonAction]
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public System.Web.Mvc.ActionResult GetDocumentThumbnailCompleted() {
        return new T4MVC_ActionResult(Area, Name, ActionNames.GetDocumentThumbnailCompleted);
    }

    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public DocumentController Actions { get { return MVC.Document; } }
    [GeneratedCode("T4MVC", "2.0")]
    public readonly string Area = "";
    [GeneratedCode("T4MVC", "2.0")]
    public readonly string Name = "Document";

    static readonly ActionNamesClass s_actions = new ActionNamesClass();
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public ActionNamesClass ActionNames { get { return s_actions; } }
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public class ActionNamesClass {
        public readonly string Index = "Index";
        public readonly string GetDocumentListCompleted = "GetDocumentListCompleted";
        public readonly string GetDocumentThumbnailCompleted = "GetDocumentThumbnailCompleted";
    }


    static readonly ViewNames s_views = new ViewNames();
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public ViewNames Views { get { return s_views; } }
    [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
    public class ViewNames {
        public readonly string Index = "~/Views/Document/Index.aspx";
    }
}

[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class T4MVC_DocumentController: WebUI.Client.Controllers.DocumentController {
    public T4MVC_DocumentController() : base(Dummy.Instance) { }

    public override System.Web.Mvc.ActionResult Index() {
        var callInfo = new T4MVC_ActionResult(Area, Name, ActionNames.Index);
        return callInfo;
    }

    public override System.Web.Mvc.JsonResult GetDocumentListCompleted(Web.Gateway.DocumentMetaDataCollection clientDocuments) {
        var callInfo = new T4MVC_JsonResult(Area, Name, ActionNames.GetDocumentListCompleted);
        callInfo.RouteValueDictionary.Add("clientDocuments", clientDocuments);
        return callInfo;
    }

    public override System.Web.Mvc.ActionResult GetDocumentThumbnailCompleted(System.IO.Stream document, string type) {
        var callInfo = new T4MVC_ActionResult(Area, Name,    ActionNames.GetDocumentThumbnailCompleted);
            callInfo.RouteValueDictionary.Add("document", document);
            callInfo.RouteValueDictionary.Add("type", type);
            return callInfo;
        }

    }
}

#endregion T4MVC
#pragma warning restore 1591

В соответствии с просьбой вот заглушки моего класса DocumentController;

namespace WebUI.Client.Controllers
{
    [Activated]
    [ConcurrentSessionDisabled]
    public partial class DocumentController : AsyncController
    {
    public readonly HashSet<string> ImageTypes = new HashSet<string>(new[] { "jpeg", "jpg", "png", "tif", "tiff", "bmp" });
    public readonly HashSet<string> IgnoredImageTypes = new HashSet<string>(new[] { "tif", "tiff" });

    public virtual ActionResult Index()
    {
        return RedirectToAction("List");
    }

    public virtual ActionResult List()
    {
        return View();
    }

    public virtual ActionResult Thumbnails(int id)
    {
        ViewData["documentId"] = id;
        return View();
    }

    public void GetDocumentListAsync()
    {
        AsyncManager.Timeout = 30000;
        AsyncManager.OutstandingOperations.Increment();

        Task.Factory.StartNew((state) => {
            Tuple<int, int> clientDetails = (Tuple<int, int>)state;

            DocumentMetaDataCollection clientDocuments = DocumentHelper.GetClientDocuments(clientDetails.Item1, clientDetails.Item2);
            AsyncManager.Parameters["clientDocuments"] = clientDocuments;

            AsyncManager.OutstandingOperations.Decrement();

        }, new Tuple<int, int>(Profile.User().ClientId, Profile.User().CrmAccountId));
    }

    public virtual JsonResult GetDocumentListCompleted(DocumentMetaDataCollection clientDocuments)
    {
        return Json(from document in clientDocuments.Documents
                    select new
                    {
                        Id = document.Id,
                        Values = document.Values,

                    }, JsonRequestBehavior.AllowGet);
    }

    public void GetDocumentFilenamesAsync(int id)
    {
        AsyncManager.Timeout = 5000;
        AsyncManager.OutstandingOperations.Increment();

        Task.Factory.StartNew((state) =>
        {
            int documentId = (int)state;

            List<string> urls = DocumentHelper.GetUrlsForDocument(documentId);

            AsyncManager.Parameters["documentId"] = documentId;
            AsyncManager.Parameters["urls"] = urls;

            AsyncManager.OutstandingOperations.Decrement();
        }, id);
    }

    public JsonResult GetDocumentFilenamesCompleted(int documentId, List<string> urls)
    {
        IDictionary<int, string> filenameToUrl = new Dictionary<int, string>();
        Regex illegalCharacters = new Regex("[^A-Za-z0-9_[-]:]");
        int idCounter = 0;

        foreach(string url in urls)
        {
            filenameToUrl.Add(++idCounter, url);
        }

        session_HoldDocumentUrls(documentId, filenameToUrl);

        return Json(from file in filenameToUrl select new {
            Name = filenameFrom(file.Value), 
            Id = file.Key
        });
    }

    private string filenameFrom(string url)
    {
        return url.Substring(url.LastIndexOf('/') + 1, url.Length - (url.LastIndexOf('/') + 1));
    }

    public void GetThumbnailAsync(int documentId, int imageId)
    {
        AsyncManager.Timeout = 5000;
        AsyncManager.OutstandingOperations.Increment();

        string url = session_GetDocumentUrls(documentId).First(f => f.Key == imageId).Value;

        Task.Factory.StartNew((state) => {
            string fileUrl = (string)state;
            thumbnailLoader(fileUrl);
        }, url);
    }

    private void thumbnailLoader(string fileUrl)
    {
        Stream document = DocumentHelper.GetDocument(fileUrl);
        string type = fileUrl.Substring(fileUrl.LastIndexOf('.') + 1, fileUrl.Length - (fileUrl.LastIndexOf('.') + 1)).ToLower();

        if (ImageTypes.Contains(type))
        {
            document = ImageHelper.ResizeImage(document, 210);
            type = "png";
        }

        AsyncManager.Parameters["document"] = document;
        AsyncManager.Parameters["type"] = type;

        AsyncManager.OutstandingOperations.Decrement();
    }

    public virtual ActionResult GetThumbnailCompleted(Stream document, string type)
    {
        switch (type)
        {
            case "png":
                return new FileStreamResult(document, "image/png");
            case "pdf":
                return File(Links.Content.Images.pdf_256_png, "image/png");
            default:
                return File(Links.Content.Images.document_256_png, "image/png");
        }
    }

    public void FileAsync(int documentId, int imageId)
    {
        if (session_GetDocumentUrls(documentId) == null)
        {
            return;
        }

        string url = session_GetDocumentUrls(documentId).First(f => f.Key == imageId).Value;

        AsyncManager.Timeout = 5000;
        AsyncManager.OutstandingOperations.Increment();

        Task.Factory.StartNew((state) =>
        {
            string fileUrl = (string)state;

            Stream document = DocumentHelper.GetDocument(fileUrl);

            string type = fileUrl.Substring(fileUrl.LastIndexOf('.') + 1, fileUrl.Length - (fileUrl.LastIndexOf('.') + 1)).ToLower();

            if (ImageTypes.Contains(type) && !IgnoredImageTypes.Contains(type))
            {
                document = ImageHelper.ConvertToPng(document);
                type = "png";
            }

            AsyncManager.Parameters["document"] = document;
            AsyncManager.Parameters["type"] = type;

            AsyncManager.OutstandingOperations.Decrement();
        }, url);
    }

    public virtual ActionResult FileCompleted(Stream document, string type)
    {
        if (document == null || type == null)
        {
            return RedirectToAction("List");
        }

        return new FileStreamResult(document, MimeHelper.Lookup("." + type));
    }
}

}

ОБНОВЛЕНИЕ 04.07.11

Привет, Дэвид. Извините, что так долго не мог ответить вам, я искал другую работу.

Мне удалось отследить проблему, и, надеюсь, вы сможете пролить на нее свет и, возможно, создать исправление.

Если вы посмотрите на второй дамп кода выше, проблема именно в нем. Проблема в том, что вы создали;

[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public System.Web.Mvc.JsonResult GetDocumentListCompleted() {
    return new T4MVC_JsonResult(Area, Name, ActionNames.GetDocumentListCompleted);
}

Я не думал, что MVC выдает ошибку, но это было так;

2011-04-07 15:59:38,921 [41] FATAL MvcApplication [(null)] — произошло неперехваченное исключение следующие методы: System.Web.Mvc.JsonResult GetDocumentListCompleted(Web.Gateway.DocumentMetaDataCollection) для типа WebUI.Client.Controllers.DocumentController System.Web.Mvc.JsonResult GetDocumentListCompleted() для типа WebUI.Client.Controllers.DocumentController в System.Web .Mvc.Async.AsyncActionMethodSelector.GetMethodByName (String methodName)

Насколько я понимаю, система MVC имеет неоднозначное совпадение с моим кодом и кодом, который вы создали.

Если я закомментирую ваш приведенный выше код, все снова заработает нормально.

Теперь вы понимаете природу или проблему? Можно ли это исправить?

Ваше здоровье,

Райан.


person Ryan Worsley    schedule 24.03.2011    source источник


Ответы (1)


Обновление (12.04.2011): хорошо, я только что опубликовал новую сборку T4MVC (2.6.51), в которой игнорируются асинхронные методы завершения. Это должно решить проблему для вас!


T4MVC не поддерживает асинхронное действие. То есть у вас конечно могут быть асинхронные действия в ваших контроллерах, но вы не можете ссылаться на них с помощью T4MVC

См. этот предыдущий вопрос по этой теме: AsyncController в MVC2 и T4MVC : они могут работать вместе?

person David Ebbo    schedule 24.03.2011
comment
Привет Дэвид, Спасибо за ответ. Я посмотрел на эту ссылку, есть ли способ заставить T4MVC игнорировать мой асинхронный контроллер? - person Ryan Worsley; 25.03.2011
comment
Я думал, что это будет игнорировать его уже. Можете ли вы обновить свой вопрос, указав больше информации о том, как выглядит ваш контроллер и действия? Мне нужен не код внутри методов, а хотя бы объявление. Кроме того, пожалуйста, включите полную ошибку, которую вы получаете. - person David Ebbo; 25.03.2011
comment
Привет, Дэвид, я обновил вопрос выше с помощью моего контроллера. В основном я не получаю ошибку, сценарий таков: 1. Код работает нормально, 2. Я сохраняю T4MVC.cs 3. Затем ваш шаблон создает новый DocumentController.generated.cs 4. Мой контроллер просто зависает при вызове методов действия ?! 5. Установка точек останова в моих методах действий никогда их не достигает?! 6. Я отменяю T4MVC, отменяя проверку из системы управления версиями. 7. Все снова работает нормально. -- Надеюсь, это поможет вам разобраться, я люблю T4MVC, и было бы отстойно пытаться его выдрать. - person Ryan Worsley; 28.03.2011
comment
Когда вы говорите о «вызове методов действий», что вы имеете в виду? Обычно методы действий не предназначены для прямого вызова, а вместо этого вызываются через маршрутизацию (за исключением псевдовызовов T4MVC, которые вы не делаете). Кроме того, вы видите какие-либо вызовы сгенерированных методов T4MVC? Вы не должны, если вы не попытаетесь вызвать их через MVC.Document (что вы не можете сделать для асинхронных методов). Если мы не сможем понять это, возможно, вам придется отправить мне образец кода с минимальным воспроизведением. - person David Ebbo; 29.03.2011
comment
Я имею в виду звонки через систему маршрутизации, конечно. Я сделаю кучу тестов на этой неделе и вернусь к вам. Райан. - person Ryan Worsley; 29.03.2011
comment
Привет, Дэвид, я добавил обновление выше, которое пытается объяснить проблему и то, как я ее обошел. Не могли бы вы взглянуть и подумать об этом? (Помечено как ОБНОВЛЕНИЕ 04.07.11) С уважением, Райан. - person Ryan Worsley; 07.04.2011