Передача интерфейса в метод действия контроллера ASP.NET MVC

В моем приложении 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)...

Но это не сработало: я получил Не могу создать экземпляр исключения интерфейса . Я думал, что экземпляр модели будет передан в действие контроллера, но, очевидно, это не тот случай.

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

6
задан Mark Bell 25 August 2010 в 09:53
поделиться

3 ответа

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

"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);
}

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

11
ответ дан 8 December 2019 в 13:43
поделиться

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

0
ответ дан 8 December 2019 в 13:43
поделиться

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

1
ответ дан 8 December 2019 в 13:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: