2009-09-09 1 views
6

Я пытаюсь получить UpdateModel для заполнения модели, которая устанавливается как только интерфейс во время компиляции. Например, у меня есть:ASP.NET MVC UpdateModel с интерфейсом

// View Model 
public class AccountViewModel { 
    public string Email { get; set; } 
    public IProfile Profile { get; set; } 
} 

// Interface 
public interface IProfile { 
    // Empty 
} 

// Actual profile instance used 
public class StandardProfile : IProfile { 
    public string FavoriteFood { get; set; } 
    public string FavoriteMusic { get; set; } 
} 

// Controller action 
public ActionResult AddAccount(AccountViewModel viewModel) { 
    // viewModel is populated already 
    UpdateModel(viewModel.Profile, "Profile"); // This isn't working. 
} 

// Form 
<form ... > 
    <input name='Email' /> 
    <input name='Profile.FavoriteFood' /> 
    <input name='Profile.FavoriteMusic' /> 
    <button type='submit'></button> 
</form> 

Также отметим, что у меня есть пользовательские модели связующего, который наследует от DefaultModelBinder используется, заполняющий IProfile с экземпляром StandardProfile в переопределен методом CreateModel.

Проблема в том, что FavoriteFood и FavoriteMusic никогда не заселены. Есть идеи? В идеале это все будет сделано в привязке к модели, но я не уверен, что это возможно без написания полностью пользовательской реализации.

Спасибо, Брайан

ответ

2

я бы проверить код ASP.NET MVC (DefaultModelBinder), но я предполагаю, что ее отражения от типа IProfile, а не экземпляр, StandardProfile.

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

Вы могли бы попробовать что-то вроде обновления BindingContext и изменения ModelType к StandardProfile и затем вызвать

bindingContext.ModelType = typeof(StandardProfile); 
IProfile profile = base.BindModel(controllerContext, bindingContext); 

В любом случае, имея пустой интерфейс странно ~


Edit: просто хочу добавить этот код выше - это просто псевдокод, вам нужно будет проверить DefaultModelBinder, чтобы точно видеть, что вы хотите записать.


Edit # 2:

Вы можете сделать:

public class ProfileModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { 
    { 
     bindingContext.ModelType = typeof(StandardProfile); 
     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

Нет необходимости, чтобы сделать модель связующего для AccountView, что один работает отлично.


Edit # 3

Испытано его, указанное связующее вещество работает, просто нужно добавить:

ModelBinders.Binders[typeof(IProfile)] = new ProfileModelBinder(); 

Ваше действие выглядит следующим образом:

public ActionResult AddAccount(AccountViewModel viewModel) { 
    // viewModel is fully populated, including profile, don't call UpdateModel 
} 

Вы можете использовать IOC при настройке связующего (например, конструктор типа).

+0

Пустой интерфейс позволяет мне повторно использовать один и тот же код кода ядра на каждом сайте, на котором я использую это, а также поставлять другой тип профиля с использованием IOC. Я мог бы попробовать базовый класс вместо интерфейса для этой цели, но я просто не знаю, что еще я могу сделать, что дает мне гибкость, которую я ищу. Я рассмотрю упомянутый вами ModelType. –

+0

Пустой базовый класс тоже вам не поможет. Итак, в вашем коде для метода CreateModel вы вызываете что-то вроде: IoC.GetInstance (), который вы подключили, чтобы вернуть новый стандартный файл? Интересно :). Я до сих пор неясно, сколько повторного использования кода вы можете получить, когда все, что использует IProfile, должно сначала перенести его в нужный класс, но я ... Я думаю, что определение типа в контексте привязки будет работать. – anonymous

+0

Повторное использование происходит из-за того, что многие контроллеры моего сайта находятся в общей сборке, которую я ссылаюсь. Каждый сайт, который я построил, ссылается на эту общую сборку для контроллеров и моделей. Затем я могу добавить дополнительные контроллеры, модели, представления и т. Д. Для каждого сайта. В этом случае мне нужно было иметь возможность определять совершенно разные поля профиля на основе каждого узла. Следовательно, здесь нужен только интерфейс. –

0

Не осматривая фактический типа позади интерфейс обсуждался здесь: http://forums.asp.net/t/1348233.aspx

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

public class AccountViewModelModelBinder : DefaultModelBinder 
{ 
    private readonly IProfileViewModel profileViewModel; 
    private bool profileBound = false; 

    public AccountViewModelModelBinder(IProfileViewModel profileViewModel) 
    { 
     this.profileViewModel = profileViewModel; 
    } 

    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     // Bind the profile 
     if (profileBound) 
      return; 

     profileBound = true; 

     bindingContext.ModelType = profileViewModel.GetType(); 
     bindingContext.Model = profileViewModel; 
     bindingContext.ModelName = "Profile"; 

     BindModel(controllerContext, bindingContext); 
    } 

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType) 
    { 
     var model = new AccountViewModel(); 
     model.Profile = profileViewModel; 

     return model; 
    } 
} 

В основном, когда модель связующее «сделано» связывание основного AccountViewModel, я затем изменить связывание контекста (как это было предложено eyston) и вызвать BindModel еще раз. Затем это связывает мой профиль. Обратите внимание, что я вызвал GetType в profileViewModel (который поставляется контейнером IOC в конструкторе). Также обратите внимание, что я включаю флаг, указывающий, была ли уже привязана модель профиля. В противном случае будет бесконечный цикл вызова OnModelUpdated.

Я не говорю, что это довольно, но он работает достаточно хорошо для моих нужд. Мне все равно хотелось бы услышать о других предложениях.

+0

см. Редактировать # 2/# 3. – anonymous