2009-09-09 11 views
6

Ich versuche UpdateModel zu erhalten, um ein Modell zu füllen, das als nur eine Schnittstelle zur Kompilierungszeit festgelegt ist. Zum Beispiel habe ich:ASP.NET MVC UpdateModel mit Schnittstelle

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

Beachten Sie auch, dass ich ein benutzerdefiniertes Modell Binder haben, die von Default erbt verwendet werden, die IProfile mit einer Instanz von Standardprofile in der überschriebenen Methode Create auffüllt.

Das Problem ist, dass FavoriteFood und FavoriteMusic nie bevölkert sind. Irgendwelche Ideen? Idealerweise würde dies alles in der Modellmappe geschehen, aber ich bin mir nicht sicher, ob es möglich ist, ohne eine komplett benutzerdefinierte Implementierung zu schreiben.

Danke, Brian

Antwort

2

würde ich habe die ASP.NET MVC-Code (Default) zu überprüfen, aber ich glaube, dass seine von der Art IProfile reflektiert und nicht die Instanz, Standardprofile.

So sucht es nach allen IProfile-Mitgliedern, die es versuchen kann zu binden, aber es ist eine leere Schnittstelle, so dass es sich als erledigt betrachtet.

Sie könnten versuchen, so etwas wie die Binding Aktualisierung und den Modelltyp zu Standardprofile ändern und dann ruft

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

Anyways, eine leere Schnittstelle mit ist seltsam ~


Edit: möchte nur hinzufügen Dieser Code ist nur Pseudo-Code, Sie müssten DefaultModelBinder überprüfen, um genau zu sehen, was Sie schreiben möchten.


Edit # 2:

Können Sie tun:

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

Keine Notwendigkeit, ein Modell Bindemittel für Account zu machen, arbeitet, dass man in Ordnung.


Edit # 3

es Getestet aus, das vorstehend genannte Bindemittel funktioniert, müssen nur hinzufügen:

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

Ihre Aktion wie folgt aussieht:

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

Sie verwenden können IOC beim Setzen des Modellbinders (z. B. den Typkonstruktor einfügen).

+0

Die leere Schnittstelle ermöglicht es mir, den gleichen Kerncode-Code auf jeder Site, auf der ich diese verwende, wiederzuverwenden, während ich einen anderen Profiltyp mit einem IOC zur Verfügung stelle. Ich könnte zu diesem Zweck vielleicht eine Basisklasse anstelle einer Schnittstelle ausprobieren, aber ich bin nur unsicher, was ich sonst noch tun kann, das gibt mir die Flexibilität, die ich suche. Ich werde mir den von Ihnen erwähnten ModelType ansehen. –

+0

Eine leere Basisklasse wird dir auch nicht viel nützen. Also in Ihrem Code für die CreateModel-Methode rufen Sie etwas wie: IoC.GetInstance (), die Sie ein neues StandardProfile zurückgegeben haben? Interessant :). Ich bin immer noch unklar, wie viel Code Wiederverwendung Sie bekommen können, wenn alles, was IProfile verwendet, muss es zuerst in die richtige Klasse zu werfen, aber yah ... Ich denke, die Angabe des Typs im Bindungskontext funktioniert. – anonymous

+0

Die Wiederverwendung kommt von der Tatsache, dass viele der Controller meiner Site in einer gemeinsamen Assembly sind, die ich referenziere. Jede Site, die ich erstelle, verweist auf diese allgemeine Assembly für Controller und Modelle. Ich kann dann zusätzliche Controller, Modelle, Ansichten usw. für jede Site hinzufügen. In diesem Fall musste ich in der Lage sein, völlig unterschiedliche Profilfelder auf einer Site-by-Site-Basis zu definieren. Daher die Notwendigkeit für nur eine Schnittstelle hier. –

0

Inspektion nicht den tatsächlichen Typ hinter der Schnittstelle hier diskutiert wurde: http://forums.asp.net/t/1348233.aspx

Das heißt, ich einen hackish Weg, um das Problem zu finden. Da ich bereits einen benutzerdefinierten Modellordner für diesen Typ hatte, konnte ich Code hinzufügen, um die Bindung für mich durchzuführen.Hier ist, was mein Modell Bindemittel wie jetzt aussieht:

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

Grundsätzlich, wenn das Modell Bindemittel „done“ die Haupt AccountViewModel Bindung, ich den Bindungskontext dann ändern (wie durch Eyston vorgeschlagen) und BindModel rufen erneut. Das bindet dann mein Profil. Beachten Sie, dass ich GetType für das profileViewModel aufgerufen habe (das vom IOC-Container im Konstruktor bereitgestellt wird). Beachten Sie auch, dass ich ein Flag einschließe, um anzugeben, ob das Profilmodell bereits gebunden wurde. Andernfalls würde eine Endlosschleife von OnModelUpdated aufgerufen werden.

Ich sage nicht, das ist schön, aber es funktioniert gut genug für meine Bedürfnisse. Ich würde immer noch gerne von anderen Vorschlägen hören.

+0

siehe Bearbeiten # 2/# 3. – anonymous