ASP.NET MVC Публикация моделей для действий с интерфейсом - PullRequest
5 голосов
/ 11 января 2012

Насколько я понимаю, ASP.NET MVC позволяет только помещать объекты POST в действия в контроллере, где аргументы действия принимают опубликованный объект как класс Concrete.

Есть ли способ обойти это илихорошая альтернатива?

В моем случае у меня есть действие, которое принимает интерфейс в качестве аргумента:

public ActionResult SaveAdjustment(IModel model)
{
    switch (model.SubsetType)
    {
        // factory like usage
    }
}

И для этого действия у меня есть многочисленные представления, все строго типизированные для объектовкоторые реализуют IModel, все, что я хочу, чтобы иметь возможность публиковать в этом одном методе.

Конечно, запуск этого дает мне ошибку:

Невозможно создать экземпляр интерфейса

Есть ли хорошая работа вокруг этого?Или мне нужно создать метод Action для каждого и отправить его такому методу?

Ответы [ 2 ]

4 голосов
/ 11 января 2012

MVC обычно связывает модели при публикации из Request.Form, то есть коллекции пар имя = значение. Причина, по которой в реализации по умолчанию отсутствует поддержка связывающих интерфейсов или абстрактных классов, очевидна - mvc не может определить, какой конкретный класс создать из пар имя = значение. Если у вас есть скрытое поле на стороне клиента или какой-либо другой параметр, с помощью которого вы можете определить, какой тип конкретного класса создать, вы можете просто создать пользовательский механизм связывания модели. Я полагаю, что вы можете переопределить метод DefaultModelBinder CreateModel и повторно использовать все другие встроенные функции связывания

public class IModelModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
    {
        //Create and return concrete instance
    }
}

И регистрация модели связующего в global.asax

ModelBinders.Binders.Add(typeof(IModel?), new IModelModelBinder());

На самом деле контроллеры и действия в mvc должны быть тонкими, а какой-то слой обслуживания должен быть толстым. Поскольку логика действий, которую вы пытаетесь реализовать, может вскоре усложниться, я бы рекомендовал перенести ее в отдельный сервис.

2 голосов
/ 19 января 2012

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

В случае, если неясно, почему я хотел пойтидля этого подхода я добавил пример того, как я могу использовать это для его OO-преимуществ.

    [HttpPost]
    public ActionResult SaveModelA(ModelA model)
    {
        return SaveModel(model);
    }

    [HttpPost]
    public ActionResult SaveModelB(ModelB model)
    {
        return SaveModel(model);
    }

    private ActionResult SaveModel(IModel model)
    {
        IExampleService exampleService;
        IRequirements requirements;

        switch (model.SubsetType)
        {
            case SubsetType.ModelA:
                myService = new ModelAService();
                requirements = new ModelARequirements
                {
                    ModelASpecificProperty = "example"
                };
                break;
            case SubsetType.ModelB:
                myService = new ModelBService();
                requirements = new ModelBRequirements
                {
                    ModelBSpecificProperty1 = "example",
                    ModelBSpecificProperty2 = "example2",
                    ModelBSpecificProperty3 = "example3"
                };
                break;                
            default:
                throw new InvalidEnumArgumentException();
        }

        var serviceResonse = exampleService.ExecuteExample(model, requirements);

        return RedirectToAction("Index", new
        {
            ExampleData = serviceResponse.ExampleDate
        });
    }

В случае, если это не ясно в коде:

ModelA : IModel
ModelB : IModel
ModelARequirements : IModelRequirements
ModelBRequirements : IModelRequirements
ModelAService : IExampleService
ModelBService : IExampleService

// and IModel defines a property SubsetType SubsetType { get; }
...