Передача интерфейса в метод действия контроллера ASP.NET MVC - PullRequest
6 голосов
/ 25 августа 2010

В моем приложении ASP.NET MVC у меня есть интерфейс, который служит шаблоном для нескольких различных моделей представлений:

public interface IMyViewModel
{
    Client Client1 { get; set; }
    Client Client2 { get; set; }

    Validator Validate();
}

Итак, мои модели представлений определены следующим образом:

public interface MyViewModel1 : IMyViewModel
{
    Client Client1 { get; set; }
    Client Client2 { get; set; }

    // Properties specific to MyViewModel1 here

    public Validator Validate()
    {
        // Do ViewModel-specific validation here
    }
}

public interface MyViewModel2 : IMyViewModel
{
    Client Client1 { get; set; }
    Client Client2 { get; set; }

    // Properties specific to MyViewModel2 here

    public Validator Validate()
    {
        // Do ViewModel-specific validation here
    }
}

Тогда у меня есть отдельное действие контроллера для проверки каждого отдельного типа с использованием привязки модели:

[HttpPost]
public ActionResult MyViewModel1Validator(MyViewModel1 model)
{
    var validator = model.Validate();

    var output = from Error e in validator.Errors
                 select new { Field = e.FieldName, Message = e.Message };

    return Json(output);
}

[HttpPost]
public ActionResult MyViewModel2Validator(MyViewModel2 model)
{
    var validator = model.Validate();

    var output = from Error e in validator.Errors
                 select new { Field = e.FieldName, Message = e.Message };

    return Json(output);
}

Это прекрасно работает, но если бы у меня было 30 различных типов моделей представления, то было быдолжно быть 30 отдельных действий контроллера, все с идентичным кодом, кроме сигнатуры метода, что кажется плохой практикой.

Мой вопрос, как я могу объединить эти действия проверки так, чтобы яможет передавать любую модель представления и вызывать ее метод Validate (), не заботясь о том, какой это тип?

Сначала я попытался использовать сам интерфейс в качестве параметра действия:

public ActionResult MyViewModelValidator(IMyViewModel model)...

Но это не сработало: я получил исключение Cannot create an instance of an interface.Я думал, что экземпляр модели будет передан в действие контроллера, но, очевидно, это не тот случай.

Я уверен, что упускаю что-то простое.Или, возможно, я только что подошел ко всему неправильно.Кто-нибудь может мне помочь?

Ответы [ 4 ]

11 голосов
/ 25 августа 2010

Причина, по которой вы не можете использовать интерфейс, заключается в сериализации. Когда приходит запрос, он содержит только пары ключ / значение строки, которые представляют объект:

"Client1.Name" = "John"
"Client2.Name" = "Susan"

Когда метод действия вызывается, среда выполнения MVC пытается создать значения для заполнения параметров метода (через процесс, называемый привязкой модели). Он использует тип параметра, чтобы определить, как его создать. Как вы заметили, параметр не может быть интерфейсом или любым другим абстрактным типом, поскольку среда выполнения не может создать его экземпляр. Нужен конкретный тип.

Если вы хотите удалить повторяющийся код, вы можете написать помощник:

[HttpPost]         
public ActionResult MyViewModel1Validator(MyViewModel1 model)         
{         
    return ValidateHelper(model);         
}         

[HttpPost]         
public ActionResult MyViewModel2Validator(MyViewModel2 model)         
{         
    return ValidateHelper(model);         
}

private ActionResult ValidateHelper(IMyViewModel model) {
    var validator = model.Validate();         

    var output = from Error e in validator.Errors         
                 select new { Field = e.FieldName, Message = e.Message };         

    return Json(output);
}

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

5 голосов
/ 31 января 2013

Вы можете проверить это: http://msdn.microsoft.com/en-us/magazine/hh781022.aspx.

Это вызвано тем, что у DefaultModelBinder нет способа узнать, какой конкретный тип IMyViewModel следует создать.Для решения этой проблемы вы создаете пользовательский механизм связывания моделей и указываете, как создавать и связывать экземпляр интерфейса.

1 голос
/ 25 августа 2010

Я думаю, я бы создал абстрактный базовый класс, который реализовал IMyViewModel.Я бы сделал Validate абстрактным методом и потребовал бы переопределения в моих конкретных моделях представления, которые унаследованы от MyAbstractViewModel.Внутри вашего контроллера вы можете работать с интерфейсом IMyViewModel, если хотите, но для связывания и сериализации действительно необходим конкретный класс для связывания.Мои $ .02.

1 голос
/ 25 августа 2010

Можно рассмотреть возможность использования базового класса вместо интерфейса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...