2015-01-12 3 views
0

у меня есть один базовый класс и два производных класса:Действие контроллера с произведенными классов

public class UserModel { 
    public int Id {get; set; } 
    public string Name {get; set; } 
    public UserType UserType {get; set;} 
} 

public class StudentModel : UserModel { 
    public string StudentProperty {get; set;} 
} 

public class TeacherModel : UserModel { 
    public string TeacherProperty {get; set;} 
} 

В мой контроллер ProfileController.cs У меня есть следующие два действия:

public virtual ActionResult Detail(int id) 
{ 
    var userModel = _userService.Get(id); 

    return view(usermodel); 
} 

public virtual ActionResult Save(UserModel userModel) 
{ 
    _userService.Save(userModel); 
} 

у меня есть один вид показ профиля как Студенты, так и Учителя. Моя проблема заключается в следующем:

при сохранении, используя действие Save (UserModel userMode), дополнительные свойства StudentModel и TeacherModel (StudentProperty и TeacherProperty соответственно), очевидно, не привязаны к UserModel. Итак, мой questio:

Каков правильный способ настройки действия контроллера, чтобы я мог передавать производный класс, или StudentModel или TeacherModel?

FYI, я попробовал обычное связующее (см. Ниже), однако, я не знаю, является ли это хорошим способом справиться с этим.

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
{ 
    var form = controllerContext.HttpContext.Request.Form; 

    switch (form["UserType"]) 
    { 
     case "student": 
      { 
       var studentModel = bindingContext.Model as StudentModel;       
       return studentModel; 
      } 
     case "Teacher": 
      { 
       var teacherModel = bindingContext.Model as TeacherModel; 
       medico.TeacherProperty = form["TeacherProperty"]; 
       return teacherModel; 
      } 
    } 

    return bindingContext.Model; 
} 
+0

FYI [Статья] (http://msdn.microsoft.com/en-us/magazine/hh781022.aspx) имеет некоторые подробности о создании пользовательских Абстрактные Модель Binder. –

+0

@StephenMuecke - Это хорошая статья, однако я не хочу, чтобы входящий запрос мог выбрать любой тип, который он хочет создать, без какого-либо вмешательства в процесс ввода. Было бы лучше, если бы AbstractBinder мог указать, какие конкретные типы, которые ему было разрешено создавать, и даже лучше, позволить ему взаимодействовать с атрибутом метода действия для их определения. –

+0

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

ответ

3

Самая большая проблема с привязкой к полиморфной модели - одна из целей безопасности. В конце концов, вы фактически позволяете клиенту контролировать, как данные интерпретируются. Вы должны быть осторожны, например, что пользователь не может изменить сообщение и сообщить серверу, что ваш UserModel на самом деле является администраторомModel и что вы теперь являетесь администратором.

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

Если, однако, это не является проблемой, то достаточно простой механизм, чтобы просто сделать это:

public virtual ActionResult Save(UserModel userModel) 
{ 
    TeacherModel tmodel = null; 
    StudentModel smodel = null; 
    if (userModel.UserType == UserType.Teacher) { 
     tmodel = new TeacherModel(); 
     UpdateModel<TeacherModel>(tmodel); 
    } 
    else { 
     smodel = new StudentModel(); 
     UpdateModel<StudentModel>(smodel); 
    } 

    _userService.Save((UserModel)tmodel ?? smodel); 
} 

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

На самом деле, лучшее решение в таком случае, предполагая, что ваш тип модели основан на каком-то механизме безопасности, заключается в создании правильной модели, основанной на роли пользователя.Итак, когда ваш запрос приходит, вы проверяете права пользователей и, если они находятся в роли «Учитель», вы создаете экземпляр объекта TeacherModel, и если они учащиеся, вы создаете экземпляр объекта StudentModel, и таким образом нет конечного пользователя может сделать, чтобы изменить, как это работает.

т.е. что-то вроде этого:

public virtual ActionResult Save(UserModel userModel) 
{ 
    TeacherModel tmodel = null; 
    StudentModel smodel = null; 
    // lookup user in database and verify the type of user they are 
    var user = UserManager.GetUser(userModel.UserId) 
    if (user.Role == "Teacher") 
     tmodel = new TeacherModel(); 
     UpdateModel<TeacherModel>(tmodel); 
    } 
    else { 
     smodel = new StudentModel(); 
     UpdateModel<StudentModel>(smodel); 
    } 

    _userService.Save((UserModel)tmodel ?? smodel); 
} 
+0

Спасибо за ваш ответ. Я думаю, что для моих нужд действительно слишком сложно добавить настраиваемое связующее, поэтому я пытался просто сделать это прямо в действии, как вы предложили. Одна из проблем, с которой я столкнулась, заключается в том, что вызовы 'UpdateModel (userModel);' или 'UpdateModel (userModel);' дать мне ошибку компиляции "Тип аргумента 'UserModel' не присваивается типу параметра 'TeacherModel' «Я предполагаю, что это потому, что TeacherModel происходит от UserModel, а не наоборот. Вы знаете, как я могу обойти это? –

+0

@GabrielLopez - да, я должен был дважды проверить это. Смотрите мое обновление –

+0

Большое вам спасибо! Это именно то, что мне нужно. –

1

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

Так что я имел модели что-то похожее на:

public abstract class Property {...} 

public class Industrial : Property {...} 

public class Commercial : Property {...} 

public abstract class Residence : Property {... } 

public class Condo : Residence {...} 

public class Residential : Residence {...} 

Вид:

@model Property 

@using(Html.BeginForm(Model.GetType().Name, "Property", ...)) 
{ 
} 

Контроллер:

public class PropertyController : Controller 
{ 
    [HttpPost] 
    public ActionResult Industrial(Industrial model) 
    { 
    ... 
    } 

    [HttpPost] 
    public ActionResult Commercial(Commercial model) 
    { 
    ... 
    } 

    // etc 
} 

Мой страх над созданием единого метода с пользовательской модели связующего что я бы начал выполнять специфичные для класса функции на основе типа, что создает очень большой метод, который равен r не подлежит сомнению для нескольких типов (что ломается Separation of Concerns).

+0

Спасибо Эрик. Я думаю, что это жизнеспособное решение, учитывая, что у меня не так много производных классов. Я не слишком беспокоюсь о том, чтобы нарушить разделение проблем, поскольку мой контроллер заполняет только модель, и мой уровень сервиса фактически вызывает разные провайдеры для управления логикой каждого производного класса. Если я не могу заставить его работать с одним действием, я поеду по этому маршруту. –

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