2009-09-09 14 views
6

Próbuję uzyskać UpdateModel do zapełnienia modelu, który jest ustawiony jako tylko interfejs podczas kompilacji. Na przykład, miećASP.NET MVC UpdateModel z interfejsem

// 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> 

Należy również zwrócić uwagę, że posiada własny wzór spoiwo, które dziedziczy DefaultModelBinder wykorzystywane podczas wypełniania IProfile z instancją StandardProfile w sposób CreateModel nadpisane.

Problem polega na tym, że FavoriteFood i FavoriteMusic nigdy nie są zapełniane. Jakieś pomysły? Najlepiej byłoby to zrobić w segregatorze, ale nie jestem pewien, czy jest to możliwe bez napisania całkowicie niestandardowej implementacji.

Dzięki, Brian

Odpowiedz

2

musiałbym sprawdzić kod ASP.NET MVC (DefaultModelBinder), ale zgaduję, że jego refleksji nad typu IProfile, a nie instancję, StandardProfile.

Poszukuje więc członków IProfile, które może spróbować powiązać, ale jest to pusty interfejs, więc uważa się za zrobiony.

Można spróbować czegoś podobnego aktualizowanie BindingContext i zmieniając ModelType do StandardProfile a następnie wywołanie

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

Anyways, mając pusty interfejs jest dziwne ~


Edycja: po prostu chcesz dodać powyższy kod jest po prostu pseudo kodem, musisz sprawdzić DefaultModelBinder, aby zobaczyć dokładnie, co chcesz napisać.


Edit # 2:

można zrobić:

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

Nie ma potrzeby, aby model wiążący dla AccountView, że działa dobrze.


Edit # 3

Przetestowano go na zewnątrz, powyżej spoiwo działa, tylko trzeba dodać:

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

Twoje działania wygląda następująco:

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

Można użyć IOC przy ustawianiu modułu wiążącego model (na przykład można podać konstruktor typu).

+0

Pusty interfejs pozwala mi na ponowne użycie tego samego kodu źródłowego w każdej witrynie, w której używam tej opcji, przy jednoczesnym zapewnieniu innego typu profilu przy użyciu IOC. Być może będę mógł wypróbować w tym celu klasę bazową zamiast interfejsu, ale nie jestem pewien, co jeszcze mogę zrobić, co daje mi elastyczność, której szukam. Zajrzę do ModelType, o którym wspomniałeś. –

+0

Pusta klasa podstawowa też ci nie pomoże. Więc w twoim kodzie dla metody CreateModel wywołujesz coś takiego: IoC.GetInstance (), którą podłączyłeś, aby zwrócić nowy StandardProfile? Ciekawy :). Wciąż nie jestem pewien, ile kodu można użyć ponownie, gdy wszystko, co używa IProfile, najpierw rzuciło go na właściwą klasę, ale tak ... Myślę, że określenie typu w kontekście powiązania zadziała. – anonymous

+0

Ponowne użycie pochodzi z faktu, że wiele kontrolerów mojej witryny znajduje się we wspólnym zestawie, do którego się odwołuję. Każda zbudowana przeze mnie strona odwołuje się do tego wspólnego zestawu dla kontrolerów i modeli. Mogę następnie dodać dodatkowe kontrolery, modele, widoki itp. Dla każdej witryny. W tym przypadku musiałem mieć możliwość definiowania całkowicie różnych pól profilu w poszczególnych witrynach. Stąd potrzeba tylko interfejsu. –

0

Nie sprawdzania rzeczywistego typu za interfejs został omówiony tu: http://forums.asp.net/t/1348233.aspx

Powiedział, że znalazłem hackish sposób wokół tego problemu. Ponieważ miałem już niestandardowy segregator modelu dla tego typu, udało mi się dodać do niego kod, aby wykonać wiązanie dla mnie.Oto co mój model spoiwo wygląda teraz:

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

Zasadniczo, kiedy spoiwo model „done” wiązanie główną AccountViewModel, ja wtedy zmienia kontekst wiążący (jak sugeruje Eyston) i wywołać BindModel ponownie. To wiąże mój profil. Zauważ, że nazwałem GetType na profileViewModel (który jest dostarczany przez kontener IOC w konstruktorze). Zwróć też uwagę, że dołączam flagę, aby wskazać, czy model profilu został już powiązany. W przeciwnym razie zostanie wywołana nieskończona pętla OnModelUpdated.

Nie mówię, że to jest ładne, ale działa wystarczająco dobrze dla moich potrzeb. Nadal chciałbym usłyszeć o innych sugestiach.

+0

patrz Edycja nr 2/3. – anonymous