2010-08-25 3 views
6

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

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

Уверен, что я пропустил что-то простое. Или, возможно, я только что подошел ко всему этому. Может кто-нибудь мне помочь?

ответ

10

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

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

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

+0

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

1

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

+0

Это приводит к аналогичному сообщению об ошибке. Он также не может создать экземпляр абстрактного типа. –

+1

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

1

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

+1

Я должен добавить еще один комментарий - я бы не передал абстрактный класс в контроллер, а конкретный класс. У вас может быть служба проверки или какой-либо другой метод, который может обрабатывать абстрактный класс или интерфейс, но для большей наглядности вам действительно нужно ожидать, что вы получаете в своем контроллере. – Hal

4

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

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

+0

+1 ИМХО - это путь, превосходит все остальные ответы (некоторые из которых, очевидно, не были протестированы в реальном мире). –

 Смежные вопросы

  • Нет связанных вопросов^_^