Search code examples
asp.net-mvc-3exceptionpostsharp

Message Popup Mvc3 with PostSharp OnException handling issue


  1. I have some trouble with returning the message popup in mvc3 when some exception is thrown .
  2. I'm using PostSharp as a global AOP framework to catch the exceptions and handle them building the text of the popup .
  3. I've extended the ActionResult to custom object that in ExecuteResult implements the RenderViewToString method that creates the right html code for the messagePopup.
  4. The MessagePopup is shown on the page but the Action continues to perform itself.

How can i stop it from continuing to perform itself ?

When it fails I catch it globally at the

namespace Aop
{
/// <summary>
/// Handles Aspect Object Programming in all the projects .
/// The attribute is being injected through Shared AssemblyInfo.cs to all the 
/// relevant Assemblies in the project.
/// The code of the class is being added to project in compilation time
/// and by that improves the response time quality
/// </summary>
[Serializable]
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class |
             AttributeTargets.Method | AttributeTargets.Constructor,
             AllowMultiple = true, Inherited = false)]
[MulticastAttributeUsage(MulticastTargets.Method, AllowMultiple = true,
                         AllowExternalAssemblies = true)]
public sealed class TraceAttribute : OnMethodBoundaryAspect
{
    [Inject]
    public IReportBLImpl _reportBL { get; set; }

    public TraceAttribute() { }

    #region Runtime semantics

    /// <summary>
    /// Handles all exception in all the project Ness.DoarKamuti exceptions
    /// </summary>
    /// <param name="eventArgs"></param>
    public override void OnException(MethodExecutionEventArgs eventArgs)
    {
    …
     DefActionResult res = DefActionResult.Create("~/Views/Shared/MessagePopup.ascx",_report , DefConstants.MessageDesign.PopUp, "messagePopupBody");

            eventArgs.ReturnValue = res;
 }

     }

Than it’s building the ActionResult of mine , after handling the message content

public class DefActionResult : ActionResult {

    public override void ExecuteResult(ControllerContext context)
    {
        DefJsonResult model = this.ActionModel;


        /* If a view name has been specified, render it */
        if (!string.IsNullOrEmpty(model.ViewName))
            model.ViewHTML = controller.RenderViewToString(model.ViewName, model.ViewModel);

        JsonResult res = new JsonResult() { Data = model, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        res.ExecuteResult(context);
    }
}

Then I’m building the response

public static class MVCExtensions
{

    public static string RenderViewToString(this Controller controller, string viewName, object viewData)
    {
        //Create memory writer
        var sb = new StringBuilder();
        var memWriter = new StringWriter(sb);

        //Create fake http context to render the view
        var fakeResponse = new HttpResponse(memWriter);

        var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
        var fakeControllerContext = new ControllerContext(
            new HttpContextWrapper(fakeContext),
            controller.ControllerContext.RouteData,
            controller.ControllerContext.Controller);

        var oldContext = HttpContext.Current;
        HttpContext.Current = fakeContext;

        //Use HtmlHelper to render partial view to fake context
        var html = new HtmlHelper(new ViewContext(fakeControllerContext,
            new FakeView(), controller.ViewData, controller.TempData, memWriter),
            new ViewPage());

        html.ViewDataContainer.ViewData = controller.ViewData;

        html.RenderPartial(viewName, viewData);

        //Restore context
        //HttpContext.Current = oldContext;

        //Flush memory and return output
        memWriter.Flush();
        return sb.ToString();
    }

After returning my Message Popup as it should , it continues to the original action as if it didn't crush , and of course crushing because the data source is not initialized .

I don’t want to handle the errors with the HandleErrorAttribute because it’s not as dynamic as PostSharp .

How can I stop the remains of the original Request ? (A remark , I am using the Telerik grid for mvc to show the data .)


Solution

  • To stop the method from proceeding as normal use args.FlowBehavior.Return. The method should do this already unless there is some other mechanism using a try/catch, but your aspect should apply itself as the outer most try/catch. You really need to look at your finale IL of the assembly using ILSpy (none of the other decompilers will see postsharp modifications at this point in time) then you will be able to see what is going on. If you have an action attribute, then I bet it has something to do with it since postsharp will modify the method but the action attribute will not so it remains the outer most controller of flow. Try FlowBehavior first.