Asp.net mvc 3 重构控制器中的Switch语句

Asp.net mvc 3 重构控制器中的Switch语句,asp.net-mvc-3,refactoring,controller,Asp.net Mvc 3,Refactoring,Controller,我目前正在开发一个MVC.NET3应用程序;我最近参加了“Bob叔叔”Martin的一个课程,这个课程启发了我(让我感到羞耻?)认真审视我目前的开发实践,特别是我的重构习惯 因此:我的许多路线符合: {controller}/{action}/{type} 其中类型通常决定要返回的ActionResult的类型,例如: public class ExportController { public ActionResult Generate(String type, String para

我目前正在开发一个MVC.NET3应用程序;我最近参加了“Bob叔叔”Martin的一个课程,这个课程启发了我(让我感到羞耻?)认真审视我目前的开发实践,特别是我的重构习惯

因此:我的许多路线符合:

{controller}/{action}/{type}

其中类型通常决定要返回的ActionResult的类型,例如:

public class ExportController
{
    public ActionResult Generate(String type, String parameters)
    {
        switch (type)
        {
            case "csv":
            //do something
            case "html":
            //do something else
            case "json":
            //do yet another thing
        }    
    }
}
有没有人成功地将“用polymorhism替换开关”重构应用于这样的代码?这是个好主意吗?如果能听到您在这种重构方面的经验,那将是非常棒的

提前谢谢

如果您想在本例中“用多态性替换开关”,可以创建三个重载的Generate()ActionResult方法。使用,使类型参数成为称为DataFormat(或其他)的强类型枚举,然后您将拥有:

 public ActionResult Generate(DataFormat.CSV, String parameters)
    {
    }

 public ActionResult Generate(DataFormat.HTML, String parameters)
    {
    }

 public ActionResult Generate(DataFormat.JSON, String parameters)
    {
    }

一旦达到这一点,您可以进一步重构,以消除控制器的重复性。

在我看来,此控制器操作是在要求自定义操作结果:

public class MyActionResult : ActionResult
{
    public object Model { get; private set; }

    public MyActionResult(object model)
    {
        if (model == null)
        {
            throw new ArgumentNullException("Haven't you heard of view models???");
        }
        Model = model;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        // TODO: You could also use the context.HttpContext.Request.ContentType
        // instead of this type route parameter
        var typeValue = context.Controller.ValueProvider.GetValue("type");
        var type = typeValue != null ? typeValue.AttemptedValue : null;
        if (type == null)
        {
            throw new ArgumentNullException("Please specify a type");
        }

        var response = context.HttpContext.Response;
        if (string.Equals("json", type, StringComparison.OrdinalIgnoreCase))
        {
            var serializer = new JavaScriptSerializer();
            response.ContentType = "text/json";
            response.Write(serializer.Serialize(Model));
        }
        else if (string.Equals("xml", type, StringComparison.OrdinalIgnoreCase))
        {
            var serializer = new XmlSerializer(Model.GetType());
            response.ContentType = "text/xml";
            serializer.Serialize(response.Output, Model);
        }
        else if (string.Equals("csv", type, StringComparison.OrdinalIgnoreCase))
        {
            // TODO:
        }
        else
        {
            throw new NotImplementedException(
                string.Format(
                    "Sorry but \"{0}\" is not a supported. Try again later", 
                    type
                )
            );
        }
    }
}
然后:

public ActionResult Generate(string parameters)
{
    MyViewModel model = _repository.GetMeTheModel(parameters);
    return new MyActionResult(model);
}

控制器不应该关心如何序列化数据。那不是他的责任。控制器不应该这样做。他应该专注于获取域模型,将它们映射到视图模型,并将这些视图模型传递到视图结果。

嗨,达林,谢谢你的回答,我喜欢“管道”方面优雅地移出控制器的事实。然而,严格地说,原来的开关还没有真正被重构,它被移动到了一个不同的结构中:)我可以看出这在几个地方对我们非常有用,所以谢谢!嗨,戴夫,这和我要找的一模一样。谢谢你关于自定义模型绑定的提示,我认为它在其他地方也会非常有用。想详细说明一下吗?如果DataFormat.CSV是一个值,那么您的方法是无效的c#。ModelBinder如何准确地确定这些重载中的哪一个是正确的?