Por favor, desculpe o post longo. Há uma pergunta, apenas tenha paciência comigo.
Um pouco de contexto
Temos um site que é necessário para se adaptar consideravelmente com base em uma variedade de configurações do usuário, o grupo ao qual o usuário pertence, de onde vem e outras coisas. Costumávamos incluir os bits relevantes no modelo para a página; portanto, se a página tivesse uma tabela que mostrasse se o usuário tinha mais de uma certa idade, no modelo, faríamos algo como:
//model
public PageModel
{
public bool ShowTable {get;set;}
}
//controller
public PageController
{
public ActionResult ShowPage()
{
var model = new PageModel() {
ShowTable = User.Age > 21
};
return View(model);
}
}
//view
@if(Model.ShowTable)
{
<table>Some Html here</table>
}
Isso rapidamente se tornou muito complicado para saber o que deveríamos mostrar para quais usuários. Para tentar resolver esse problema, centralizamos toda a lógica sobre quando uma coisa específica deve ser mostrada ou ocultada. Chamamos essa classe UserConfiguration
e ela (principalmente) continha apenas uma série de funções retornando booleanos indicando o que deveria ser mostrado. Isso nos permitiu configurar uma série de especificações e testes sobre o que um usuário deve ser mostrado. Isso UserConfigratuion
foi colocado em uma classe base, da qual todos os modelos de página foram herdados; portanto, o que temos atualmente é algo como isto:
//UserConfiguration
public UserConfiguration
{
private readonly User _user;
public UserConfiguration(User user) {
_user = user
}
public bool ShowTable() {
return _user.Age > 21;
}
}
//model base
public ModelBase
{
public UserConfiguration {get;set;}
}
//model
public PageModel : ModelBase
{
// whatever data is needed for the page
}
//controller
public PageController
{
public ActionResult ShowPage()
{
var userConfiguration = new UserConfiguration(User);
var model = new PageModel {
UserConfiguration = userConfiguration
};
return View(model);
}
}
//view
@if(Model.UserConfiguration.ShowTable())
{
<table>Some Html here</table>
}
Isso ajudou, principalmente porque nos permitiu criar uma série de testes sobre o que um usuário deve ou não ver etc. No entanto, não é uma solução muito limpa, tendo que reunir essa classe adicional e incluí-la no modelo. Ele também possui ramificações para renderizar vistas parciais. Se o modelo possui uma propriedade IEnumerable<Foo> Foos
que queremos renderizar em parte, mas essa parcial também depende da configuração do usuário, temos um problema. Você não pode simplesmente passar os foos para o Parcial como modelo, porque o parcial não tem acesso ao UserConfiguration
. Então, qual seria a melhor maneira de acessar essas informações. Do meu ponto de vista, no contexto do asp.net MVC, existem 4 maneiras disponíveis:
1) Tenha um novo modelo para o parcial, por exemplo
// parent view
@{
var foosModel = new foosModel {
Foos = Model.Foos,
UserConfiguration = Model.UserConfiguration
}
}
@Html.RenderPartial("FooList", foosModel)
// child partial view
@if(Model.UserConfiguration.ShowTable) {
foreach(var foo in Model.Foos) {
//Some HTML
}
}
Esta é provavelmente a solução "mais pura", aderindo melhor aos princípios do MVC, mas envolve muitos modelos (indiscutivelmente desnecessários), causando inchaço no projeto.
2) Exponha a configuração do usuário através do ViewData. por exemplo :
// parent view
@Html.RenderPartial("FooList", Model.Foos, new ViewDataDictionary { { "UserConfiguration", Model.UserConfiguration } })
// child partial view
@{
var userConfig = (UserConfiguration)ViewData["UserConfiguration"];
}
@if(userConfig.ShowTable) {
foreach(var foo in Model) {
//Some HTML
}
}
Eu realmente não gosto disso porque não é do tipo seguro e depende de seqüências de caracteres mágicas para obtê-lo no ViewData.
3) Coloque a UserConfiguration no ViewBag. Os mesmos problemas acima
4) Modifique o modelo da página e exponha a UserConfiguration por meio de uma propriedade da própria página, conforme http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view .aspx /
Sinto que, uma vez que o UserConfiguration é uma informação contextual do ambiente, faz sentido expô-lo por meio da classe, como na opção 4 acima. Existe uma prática recomendada geralmente aceita no MVC para expor esse tipo de dados? Alguém já tentou algo como a opção 4 no passado e houve algum 'pegadinha'
tl; dr: Qual é a melhor maneira de expor informações contextuais às visualizações no seu site, no MVC em geral ou no asp.net MVC em particular?