T4MVC AsyncController
I've recently installed T4MVC using Nuget. I had an old version before this that worked fine, however I had a new requirement that needed an asynchronous controller. After creating the asynchronous controller my project would not compile due to something to do with t4. So I updated using Nuget.
The problem I have now is that my controller worked fine until I recently refreshed t4mvc. After refreshing my code compiles, but when I call the async action it returns after a long time and returns a massive amount of incorrect data. Furthermore and critically, if I set breakpoints in my controller, it no longer hits them?! If I revert my DocumentController.generated.cs to the last working version everything works fine.
Below are my old DocumentController.generated.cs that works and the new one that doesn't.
Can anyone help me figure out what's going on here? I've become reliant on T4MVC because it's so good, however I really can't avoid these async actions.
OLD
// <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
NEW
// <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
As requested here's the stubs of my DocumentController class;
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 = ImageHelp开发者_如何学Pythoner.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));
}
}
}
UPDATE 7/4/11
Hi David, Apologies for taking so long to get back to you, I've been looking into some other work.
I've managed to track down the problem and hopefully you can shed some light on it and possibly create a fix.
If you look at the second code dump above, this is the one with the problem. The problem is that you've created;
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public System.Web.Mvc.JsonResult GetDocumentListCompleted() {
return new T4MVC_JsonResult(Area, Name, ActionNames.GetDocumentListCompleted);
}
I didn't think MVC was throwing an error, but it was;
2011-04-07 15:59:38,921 [41] FATAL MvcApplication [(null)] - An uncaught exception occurred System.Reflection.AmbiguousMatchException: Lookup for method 'GetDocumentListCompleted' on controller type 'DocumentController' failed because of an ambiguity between the following methods: System.Web.Mvc.JsonResult GetDocumentListCompleted(Web.Gateway.DocumentMetaDataCollection) on type WebUI.Client.Controllers.DocumentController System.Web.Mvc.JsonResult GetDocumentListCompleted() on type WebUI.Client.Controllers.DocumentController at System.Web.Mvc.Async.AsyncActionMethodSelector.GetMethodByName(String methodName)
So as I understand it, the MVC system has an ambiguous match with my code and the code you've created.
If I comment out your above code then everything runs normally again.
Do you understand the nature or the problem now? Can it be fixed?
Cheers,
Ryan.
Update (4/12/2011): ok, I just posted a new build of T4MVC (2.6.51) that ignored async completion methods. This should fix the issue for you!
T4MVC doesn't support async action. That is, you can certainly have async actions in your controllers, but you cannot refer to them using T4MVC
Please see this previous question on that topic: AsyncController in MVC2 and T4MVC: can they work together?
精彩评论