Pergunta

A partir esta questão , parece que faz sentido ter um controlador de criar um ViewModel que reflete com mais precisão o modelo que a visão está tentando exibição, mas estou curioso sobre algumas das convenções (Eu sou novo para o padrão MVC, se não fosse já óbvia).

Basicamente, eu tinha as seguintes perguntas:

  1. I normalmente como para ter uma classe / arquivo. Isso faz sentido com um ViewModel se ele só está sendo criado para mão de dados de um controlador para uma visão?
  2. Se um ViewModel pertence em seu próprio arquivo, e você estiver usando uma estrutura de diretório / projeto para manter as coisas separadas, onde é que o ViewModel arquivo pertence? Nos Controladores diretório?

É basicamente isso por agora. Eu poderia ter mais algumas perguntas chegando, mas isso foi me incomodando para a última hora ou assim, e eu consigo encontrar orientação consistente em outro lugar.

EDIT: Olhando para a amostra NerdDinner aplicativo no CodePlex, parece que os ViewModels fazem parte do Controladores , mas ainda faz-me desconfortável que eles não estão em seus próprios arquivos

Foi útil?

Solução

Eu criar o que eu chamo de "ViewModel" para cada vista. Eu colocá-los em uma pasta chamada ViewModels no meu projeto MVC Web. Eu nomeá-los depois que o controlador e ação (ou vista) que representam. Então, se eu preciso para passar dados para o ponto de vista SignUp no controlador Membership eu criar uma classe MembershipSignUpViewModel.cs e colocá-lo na pasta ViewModels.

Então eu adicionar as propriedades e métodos necessários para facilitar a transferência de dados a partir do controlador para a vista. Eu uso o AutoMapper para obter do meu ViewModel para o modelo de domínio e vice-versa, se necessário.

Isso também funciona bem para ViewModels compostos que contêm propriedades que são do tipo de outros ViewModels. Por exemplo, se você tem 5 widgets na página de índice no controlador de adesão, e você criou um ViewModel para cada vista parcial - como você passar os dados a partir da ação Índice para os parciais? Você adiciona uma propriedade à MembershipIndexViewModel do tipo MyPartialViewModel e quando tornando a parcial você deve passar em Model.MyPartialViewModel.

Fazendo dessa forma, permite ajustar as propriedades parciais ViewModel sem ter que mudar o ponto de vista Índice de todo. Ainda assim passa em Model.MyPartialViewModel para que haja menos de uma chance de que você vai ter que passar por toda a cadeia de parciais para corrigir alguma coisa quando tudo que você está fazendo é adicionar uma propriedade para o ViewModel parcial.

Eu também irá adicionar o namespace "MyProject.Web.ViewModels" para o web.config de modo a permitir-me fazer referência a eles em qualquer ponto de vista, sem nunca adicionar uma instrução de importação explícita em cada vista. Apenas o torna um pouco mais limpo.

Outras dicas

Separar aulas por categoria (Controladores, ViewModels, filtros etc.) é um disparate.

Se você quiser escrever código para a seção inicial de seu website (/), em seguida, crie uma pasta chamada Home e colocar lá o HomeController, IndexViewModel, AboutViewModel, etc, e todas as classes relacionados usado por ações Internos.

Se você compartilhou aulas, como um ApplicationController, você pode colocá-lo na raiz do seu projeto.

Por que separar as coisas que estão relacionados (HomeController, IndexViewModel) e manter as coisas em conjunto que não têm relação alguma (HomeController, AccountController)?


Eu escrevi um blog sobre este tema.

Eu mantenho minhas aulas de aplicação em uma pasta sub chamado "núcleo" (ou uma biblioteca de classe separada) e usar os mesmos métodos como o aplicação de exemplo Kigg mas com algumas pequenas mudanças para fazer minhas aplicações mais DRY.

I criar uma classe BaseViewData em / Core / ViewData / onde eu armazenar local comum propriedades de largura.

Depois disto, eu também criar todas as minhas aulas vista ViewData na mesma pasta que depois derivar de BaseViewData e têm propriedades vista específicos.

Então eu criar um ApplicationController que todos os meus controladores derivam. O ApplicationController tem um método GetViewData genérico da seguinte forma:

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

Finalmente, na minha ação do controlador eu faço o seguinte para construir o meu ViewData Modelo

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

Eu acho que isso funciona muito bem e mantém os seus pontos de vista arrumado e seus controladores magro.

A classe ViewModel está lá para encapsular várias partes de dados representados por instâncias de classes em um fácil objeto para gerenciar que você pode passar para o seu modo de exibição.

Não faria sentido ter suas classes ViewModel em seus próprios arquivos, no próprio diretório. Em meus projetos eu tenho uma sub-pasta dos Modelos pasta chamada ViewModels. É aí que meus ViewModels (por exemplo ProductViewModel.cs) ao vivo.

Não há bom lugar para manter seus modelos. Você pode mantê-los em assembly separado se o projeto é grande e há um monte de ViewModels (transferência de objetos de dados). Além disso, você pode mantê-los na pasta separada do projeto local. Por exemplo, em Oxite eles são colocados em projeto Oxite que contém um monte de várias classes também. Controladores em Oxite são movidos para projeto separado e vistas estão em projeto separado também.
Em CodeCampServer ViewModels são nomeados * Forma e eles são colocados em projeto de interface do usuário na pasta Modelos.
Em MvcPress projeto eles são colocados em projeto de Data, que também contém todo o código para trabalhar com banco de dados e um pouco mais (mas eu não recomendo essa abordagem, é apenas para uma amostra)
Então você pode ver, existem muitos pontos de vista. Eu costumo manter minhas ViewModels (DTO objetos) no projeto local. Mas quando eu tiver mais de 10 modelos I preferem para movê-los para separar montagem. Normalmente, neste caso, eu estou movendo os controladores para separar montagem também.
Outra questão é como mapear facilmente todos os dados de modelo para seu ViewModel. Eu sugiro dar uma olhada em AutoMapper biblioteca. Eu gosto muito dele, ele faz todo o trabalho sujo para mim.
E eu também sugiro que olhar para SharpArchitecture projeto . Ele fornece muito boa arquitetura para projetos e contém uma grande quantidade de quadros legais e orientações e grande comunidade.

aqui está um trecho de código de meus melhores práticas:

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}

Nós jogamos todos os nossos ViewModels nos modelos pasta (toda a nossa lógica de negócios está em um projeto ServiceLayer separado)

Pessoalmente, eu sugiro que se o ViewModel não é nada trivial, em seguida, usar uma classe separada.

Se você tem mais de um modelo de visão, então eu sugiro que faz sentido para particionar-lo em pelo menos um diretório. se o modelo de vista mais tarde é compartilhada então o espaço de nome implícita no diretório torna mais fácil para se deslocar para uma nova montagem.

No nosso caso, temos os modelos, juntamente com os controladores em um projeto separado a partir das vistas.

Como regra geral, nós tentamos mover e evitar a maior parte do ViewData [ "..."] coisas para o ViewModel, assim, evitamos castings e cordas mágicas, que é uma coisa boa.

O ViewModel também mantém algumas propriedades comuns, como informações de paginação para listas ou informações de cabeçalho da página para chamar a farinha de rosca e títulos. Neste momento a classe base contém informações demais na minha opinião e podemos dividi-lo em três pedaços, a informação mais básica e necessária para 99% das páginas de um modelo de vista de base, e, em seguida, um modelo para as listas e um modelo para as formas que possuem dados específicos para que os cenários e herdam a uma base.

Por fim, vamos implementar um modelo de vista para cada entidade para lidar com a informação específica.

código no controlador:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

código no modelo de vista:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

projetos:

  • DevJet.Web (Web ASP.NET MVC projeto)

  • DevJet.Web.App.Dictionary (a projeto de biblioteca de classe separada)

    Neste projeto, eu fiz algumas pastas como: DAL, BLL, BO, VM (pasta para modelos de exibição)

Criar uma classe base de vista de modelo que tem habitualmente exigidos imóveis como resultado da operação e dados contextuais, você também pode colocar os dados do usuário atuais e papéis

class ViewModelBase 
{
  public bool HasError {get;set;} 
  public string ErrorMessage {get;set;}
  public List<string> UserRoles{get;set;}
}

Na classe controlador de base tem um método como PopulateViewModelBase () este método irá preencher os dados contextuais e funções de usuário. O HasError e ErrorMessage, definir essas propriedades se há excepção enquanto puxa dados de serviço / db. Bind essas propriedades no intuito de mostrar erro. funções de usuário pode ser usado para mostrar hide seção sobre visão baseada em funções.

Para preencher modelos de exibição em diferentes ações GET, ele pode ser feito consistente por ter controlador de base com método abstrato FillModel

class BaseController :BaseController 
{
   public PopulateViewModelBase(ViewModelBase model) 
{
   //fill up common data. 
}
abstract ViewModelBase FillModel();
}

Em controladores

class MyController :Controller 
{

 public ActionResult Index() 
{
   return View(FillModel()); 
}

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top