C# Function as parameter - Repeat method structure

时间:2016-08-31 18:39:47

标签: javascript c# .net asp.net-mvc delegates

I have multiple methods with different signature, and each method has a try-catch block with custom log exception. (Same structure on multiple controllers).

public class TestController : BaseController
{
    public static ActionResult One(int param1, string param2)
    {
        try
        {
            // Do something
        }
        catch (Exception e)
        {
            LogException(e.Message);
            AddModelError(e.Message);
        }
        return View("ViwName1");
    }

    public static ActionResult Two(Date param3, bool param4)
    {
        try
        {
            // Do something
        }
        catch (Exception e)
        {
            LogException(e.Message);
            AddModelError(e.Message);
        }
        return View("ViwName2");
    }
}

I wonder if there's a way to avoid try-catch block for every method and execute another

public class TestController : BaseController
{
    public static ActionResult One(int param1, string param2)
    {
        // Do something (*)
        // Call "ActionWithTryCatch" method that has a "function argument" to "Do something (*)"
    }

    public ActionResult ActionWithTryCatch(MyDelegate del, string viewName)
    {
        try
        {
            return del.Invoke();
        }
        catch (Exception e)
        {
            LogException(e.Message);
            AddModelError(e.Message);
        }
        return View(viewName);
    }
}

¿How can I do that? I've seen examples using delegates but I understand that's strongly typed, so didn't find a way to do that. Thanks!

2 个答案:

答案 0 :(得分:1)

您所描述的模式接近于面向方面编程(AOP)的形式。但是,如果您只想对控制器上的所有操作应用特定的Try Catch错误处理逻辑,那么您可能不值得为其提供整个AOP框架。相反,您可以利用HandleErrorAttribute或覆盖控制器类的OnException方法。

例如,您可以像这样编写控制器:

public class TestController
{
    private TestService service;

    public TestController(TestService service)
    {
        this.service = service;
    }

    public ActionResult One(int param1, string param2)
    {
        this.service.MethodOne(param1, param2);
        return View("ViwName1");
    }

    public ActionResult Two(Date param3, bool param4)
    {
        this.service.MethodTwo(param3, param4);
        return View("ViwName2");
    }

    protected override void OnException(ExceptionContext filterContext)
    {
        LogException(filterContext.Exception.Message);
        AddModelError(filterContext.Exception.Message);

        var errorView = new ViewResult { ViewName = "~/Path/To/Error/View" };
        filterContext.Result = errorView;
    }
}

如果你想要将它抽象出来,那么你可以将重写的OnException逻辑移动到基本控制器类中,然后让所有控制器继承自基本控制器。

如果您想在MVC中看到一些统一错误处理的其他方法,请查看此博客:https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging

<强>更新

根据我的评论,如果你坚持实施你所描述的模式,你可以使用gilmishal答案的这个修改版本。

public class TestController
{
    private TestService service;

    public TestController(TestService service)
    {
        this.service = service;
    }

    public ActionResult One(int param1, string param2)
    {
        return this.ActionWithTryCatch(() => this.service.MethodOne(param1, param2), "ViwName1");
    }

    public ActionResult Two(Date param3, bool param4)
    {
        return this.ActionWithTryCatch(() => this.service.MethodTwo(param3, param4), "ViwName2");
    }

    public IActionResult ActionWithTryCatch(Action action, string viewName)
    {
        try
        {
            action.Invoke();
        }
        catch (Exception e)
        {
            LogException(e.Message);
            AddModelError(e.Message);
        }

        return View(viewName);
    }
}

答案 1 :(得分:0)

this method you created won't work properly if you need to pass parameters to this function in js - so I am assuming you are calling only parameterless methods.

in that case you could use Func<IActionResult> instead of MyDelegate.

public TResult ActionWithTryCatch<TResult>(Func<TResult> del, string viewName)
{
    try
    {
        return del.Invoke();
    }
    catch (Exception e)
    {
        LogException(e.Message);
        AddModelError(e.Message);
        throw;
    }
}

this will be more similar to your javascript implementation, and will return a 500 http result upon unhandledexception.

You should call it like this, if you want IActionResult return type -

ActionWithTryCatch<IActionResult>(MethodThatReturnsIActionResult, viewName);

I recommand you look into generics