Un legante modello personalizzato a CreateModel e un altro a BindModel?
-
27-10-2019 - |
Domanda
Sfondo:Nei miei metodi di azione post back MVC che sto ricevendo comando oggetti piuttosto che visualizzare i modelli. L'idea è che questi oggetti di comando (che equivalgono approssimativamente agli script delle transazioni) saranno impostati e pronti per essere eseguiti all'ingresso del metodo di azione, con il legante del modello che ha parametri impostati che vengono utilizzati durante il processo di esecuzione:
public class MyCommand : IMyCommand
{
// In this case Value1 and Value2 are being set by the default model binder from posted form values - wonderful :)
public String Value1 { get; set; }
public String Value2 { get; set; }
public CommandResult ExecuteCommand()
{
// Does something awesome...
}
}
Per rendere le cose un po 'più complesse, i miei oggetti di comando hanno dipendenze (servizi, repository ecc.) Che sono richiesti nei rispettivi costruttori; Quindi ho dovuto creare un legante modello personalizzato che utilizzava la dipendenza predefinita Resolver (che era già impostata con il mio contenitore IOC) per costruire gli oggetti del modello:
public class DependencyModelBinder : DefaultModelBinder
{
protected override Object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
return DependencyResolver.Current.GetService(modelType);
}
}
E impostare Global.asax.cs
così:
ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();
Ancora una volta tutto funziona bene, le dipendenze vengono iniettate nel costruttore e quindi il legante del modello predefinito prende il sopravvento per impostare le proprietà come al solito.
Il problema:Il problema che ho è che tutti i miei oggetti di comando hanno un parametro GUID "SessionID" (che proviene da un cookie) e la prima cosa che fanno è provare a risolvere un oggetto di sessione da questo ID usando un servizio iniettato.
public class MyCommand : IMyCommand
{
public MyCommand (ISessionRepository sessionRepository) { ... }
public Guid SessionId { get; set; } // Set by model binder from a cookie...
public CommandResult Execute()
{
Session session = SessionRepository.Get(SessionId);
if (session == null)
// Do something not so awesome...
}
}
Volevo rimuovere questa ripetizione, quindi ho creato un secondo legante modello che si sarebbe preso cura di questa ricerca nel repository, il che significa che i miei oggetti di comando potrebbero avere un Session
Proprietà direttamente (rimozione della dipendenza dal costruttore per il repository della sessione).
public class SessionModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var sessionRepository = DependencyResolver.Current.GetService<ISessionRepository>();
return sessionRepository.Get((Guid)controllerContext.HttpContext.Request["SessionId"]);
}
}
Mio Global.asax.cs
File ora sembra così:
ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();
ModelBinders.Binders.Add(typeof(Session), new SessionModelBinder());
Dopo aver testato il moderno sessione in isolamento, so che funziona. Tuttavia, quando lo usi insieme al DipendencyModelbinder, non viene mai chiamato. Come posso convincere MVC a utilizzare la mia DipendencyModelbinder durante la costruzione di oggetti del modello, ma ho usato la mia session modelbinder quando le proprietà di sessione di legame su di essi? O qualcuno sa un approccio migliore a questo?
Soluzione
È possibile utilizzare il metodo GetPropertyValue nel legante del modello originale per fornire un valore per la proprietà della sessione:
public class DependencyModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
return DependencyResolver.Current.GetService(modelType);
}
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
if (propertyDescriptor.Name == "Session")
{
var sessionRepository = DependencyResolver.Current.GetService<ISessionRepository>();
return sessionRepository.Get(controllerContext.HttpContext.Request["SessionId"]);
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}