Dois modelos em uma visualização no ASP MVC 3

91

Tenho 2 modelos:

public class Person
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }
}
public class Order
{
    public int OrderID { get; set; }
    public int TotalSum { get; set; }
}

Quero editar objetos de AMBAS as classes na visualização SINGLE, então preciso de algo como:

@model _try2models.Models.Person
@model _try2models.Models.Order

@using(Html.BeginForm())
{
    @Html.EditorFor(x => x.PersonID)
    @Html.EditorFor(x => x.PersonName)
    @Html.EditorFor(x=>x.OrderID)
    @Html.EditorFor(x => x.TotalSum)
}

Isso, é claro, não funciona: apenas uma instrução 'model' é permitida em um arquivo .cshtml. Pode haver alguma solução alternativa?

Smarty
fonte
1
Minha resposta ajuda você?
Andrew,
usei ViewBagcom for each in view funcionou para mim, verifique isso para saber várias opções, economizei pouco tempo para mim em vez de criar um modelo de visualização ou visualização parcial
shaijut

Respostas:

118

Crie um modelo de vista pai que contenha os dois modelos.

public class MainPageModel{
    public Model1 Model1{get; set;}
    public Model2 Model2{get; set;}
}

Desta forma, você pode adicionar modelos adicionais em uma data posterior com o mínimo de esforço.

Andrew
fonte
2
Ao usar esta solução, lembre-se de que as alterações no Model1 ou Model2 podem afetar o seu MainPageModel. Além disso, eles contêm mais dados do que a visualização realmente precisa. Se sua MainPage for uma combinação de coisas que já estão em outros controladores, alternar de RenderPartial para RenderAction permitirá que você mantenha as coisas bem separadas. Considere a possibilidade de ler sobre a Lei de Demeter: en.wikipedia.org/wiki/Law_of_Demeter
Fenton
1
@Andi - Eu criei o modelo como você sugeriu acima. Mas quando eu clico com o botão direito no controlador e tento criar o controlador Crud, ele não está funcionando? Alguma sugestão? Como posso criar um controlador crud com visão automática para o modelo acima?
NoviceMe
2
De todas as abordagens que vi para resolver esse problema, esta é a que funcionou melhor para mim. É de longe a solução mais simples que funciona.
Ciaran Gallagher
4
O momento em que você se pergunta "Por que não pensei nisso antes?" Ótima solução
Rafael AMS
1
@Andi Como eu exporia os métodos do respectivo modelo no controlador?
Volatil3 de
53

Para usar a tupla, você precisa fazer o seguinte; na visualização, altere o modelo para:

@model Tuple<Person,Order>

para usar métodos @html, você precisa fazer o seguinte, ou seja:

@Html.DisplayNameFor(tuple => tuple.Item1.PersonId)

ou

@Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |

Item1 indica o primeiro parâmetro passado para o método Tupla e você pode usar o Item2 para acessar o segundo modelo e assim por diante.

em seu controlador, você precisa criar uma variável do tipo Tupla e, em seguida, passá-la para a visualização:

    public ActionResult Details(int id = 0)
    {
        Person person = db.Persons.Find(id);
        if (person == null)
        {
            return HttpNotFound();
        }
        var tuple = new Tuple<Person, Order>(person,new Order());

        return View(tuple);
    }

Outro exemplo: vários modelos em uma vista

Hamid Tavakoli
fonte
1
Nunca use um Tupleneste caso - OP está querendo editar dados e um Tuplenão tem construtor padrão, então o modelo não pode ser vinculado quando o formulário é enviado
Nunca diga nunca. Isso é um pouco forte aqui.
johnny
47

Outra opção que não precisa criar um modelo customizado é usar uma Tupla <> .

@model Tuple<Person,Order>

Não é tão limpo quanto criar uma nova classe que contenha ambos, conforme a resposta de Andi, mas é viável.

Bobson
fonte
no mvc 3 dá erro. Apenas uma instrução 'model' é permitida em um arquivo. Alguma idéia de como resolvê-lo?
GibboK
1
@GibboK - Estou usando em MVC3 muito bem. Certifique-se de não ter duas @modellinhas separadas ?
Bobson,
3
Você pode obter as propriedades de cada modelo com: @Model.Item1.Propertye @Model.Item2.Propertypara Persone Orderrespectivamente
usuário2019515
1
Eu usei Tupla em minha visão. Mas, não consigo obter os valores do modelo no meu controlador. Mas, enquanto utilizo o modelo de visualização Parent como recomendado por @Andi, obtive os valores do modelo no meu controlador.
Naren
1
@StephenMuecke - Hmm. Eu nunca havia considerado isso, mas você está certo. Outra razão pela qual a resposta de Andrew é a melhor para uso geral. Quando você vai com uma alternativa leve (como este), você não desistir de funcionalidade.
Bobson
10

Se você é fã de modelos muito planos, apenas para apoiar a visualização, você deve criar um modelo específico para esta visualização em particular ...

public class EditViewModel
    public int PersonID { get; set; }
    public string PersonName { get; set; }
    public int OrderID { get; set; }
    public int TotalSum { get; set; }
}

Muitas pessoas usam o AutoMapper para mapear de seus objetos de domínio para suas visualizações planas.

A ideia do modelo de visualização é que ele apenas suporta a visualização - nada mais. Você tem um por visualização para garantir que ela contenha apenas o que é necessário para aquela visualização - não um monte de propriedades que você deseja para outras visualizações.

Fenton
fonte
5

ok, todo mundo está fazendo sentido e eu peguei todas as peças e coloquei aqui para ajudar novatos como eu que precisam de explicação do começo ao fim.

Você faz sua grande classe com 2 classes, de acordo com a resposta de @Andrew.

public class teamBoards{
    public Boards Boards{get; set;}
    public Team Team{get; set;}
}

Então em seu controlador você preenche os 2 modelos. Às vezes, você só precisa preencher um. Então, no retorno, você referencia o modelo grande e ele levará o 2 de dentro para a View.

            TeamBoards teamBoards = new TeamBoards();


        teamBoards.Boards = (from b in db.Boards
                               where b.TeamId == id
                               select b).ToList();
        teamBoards.Team = (from t in db.Teams
                              where t.TeamId == id
                          select t).FirstOrDefault();

 return View(teamBoards);

No topo da visualização

@model yourNamespace.Models.teamBoards

Em seguida, carregue suas entradas ou exibições que fazem referência aos grandes conteúdos dos Modelos:

 @Html.EditorFor(m => Model.Board.yourField)
 @Html.ValidationMessageFor(m => Model.Board.yourField, "", new { @class = "text-danger-yellow" })

 @Html.EditorFor(m => Model.Team.yourField)
 @Html.ValidationMessageFor(m => Model.Team.yourField, "", new { @class = "text-danger-yellow" })

E. . . .de volta ao rancho, quando o Post chegar, faça referência à Grande Classe:

 public ActionResult ContactNewspaper(teamBoards teamboards)

e fazer uso do (s) modelo (s) retornado (s):

string yourVariable = teamboards.Team.yourField;

Provavelmente tem algum material de Validação de DataAnnotation na classe e provavelmente coloque if (ModelState.IsValid) no topo do bloco de salvar / editar. . .

JustJohn
fonte
4

Na verdade, existe uma maneira de usar dois ou mais modelos em uma visualização sem envolvê-los em uma classe que contenha ambos.

Usando Employee como um modelo de exemplo:

@model Employee

É realmente tratado como.

@{ var Model = ViewBag.model as Employee; }

Portanto, o método View (funcionário) está configurando seu modelo para o ViewBag e, em seguida, o ViewEngine o está projetando.

Isso significa que,

ViewBag.departments = GetListOfDepartments();
    return View(employee);

Pode ser usado como,

            @model  Employee
        @{
                var DepartmentModel = ViewBag.departments as List<Department>;
        }

Essencialmente, você pode usar o que quer que esteja em seu ViewBag como um "Modelo" porque é assim que funciona de qualquer maneira. Não estou dizendo que isso seja arquitetonicamente ideal, mas é possível.

Alex Tselevich
fonte
3

Basta criar um único modelo de visão com todas as informações necessárias nele, normalmente o que eu faço é criar um modelo para cada visão para que eu possa ser específico em cada visão, ou fazer um modelo pai e herdá-lo. OU faça um modelo que inclua ambas as vistas.

Pessoalmente, eu apenas os adicionaria em um modelo, mas é assim que eu faço:

public class xViewModel
{
    public int PersonID { get; set; }
    public string PersonName { get; set; }
    public int OrderID { get; set; }
    public int TotalSum { get; set; }
}

@model project.Models.Home.xViewModel

@using(Html.BeginForm())
{
    @Html.EditorFor(x => x.PersonID)
    @Html.EditorFor(x => x.PersonName)
    @Html.EditorFor(x => x.OrderID)
    @Html.EditorFor(x => x.TotalSum)
}
Michael
fonte
1

Você pode usar o padrão de apresentação http://martinfowler.com/eaaDev/PresentationModel.html

Este modelo de "Visualização" de apresentação pode conter Person e Order, esta nova
classe pode ser o modelo de referência de sua visualização.

James Kyburz
fonte
1
O modelo de apresentação deve conter, de fato, os dados necessários, que podem ser provenientes de uma Pessoa e de um Pedido. Na verdade, ele não deve conter os objetos de domínio Pessoa ou Pedido. A visualização separa a exibição do domínio subjacente.
Fenton
0

Outra maneira que nunca falamos é criar uma visão em MSSQL com todos os dados que você deseja apresentar. Em seguida, use LINQ to SQL ou qualquer outro para mapeá-lo. Em seu controlador, retorne-o à visualização. Feito.

escondido
fonte
0

você não pode declarar dois modelos em uma vista, tente usar Html.Action("Person", "[YourController]")& Html.Action("Order", "[YourController]").

Boa sorte.

Jimmy Mac
fonte
O que isso faz?
johnny
0

Ao lado de um modelo de visualização em asp.net, você também pode fazer múltiplas visualizações parciais e atribuir diferentes visualizações de modelo a cada visualização, por exemplo:

   @{
        Layout = null;
    }

    @model Person;

    <input type="text" asp-for="PersonID" />
    <input type="text" asp-for="PersonName" />

em seguida, outro modelo de vista parcial para o modelo de pedido

    @{
        Layout = null;
     }

    @model Order;

    <input type="text" asp-for="OrderID" />
    <input type="text" asp-for="TotalSum" />

então, em sua visualização principal, carregue ambas as visualizações parciais por

<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
Liam
fonte
-1

Espero que você ache útil !!

eu uso o ViewBag para projeto e modelo para a tarefa, então, desta forma, estou usando dois modelos em visualização única e no controlador eu defini o valor ou dados do viewbag

List<tblproject> Plist = new List<tblproject>();
            Plist = ps.getmanagerproject(c, id);

            ViewBag.projectList = Plist.Select(x => new SelectListItem
            {
                Value = x.ProjectId.ToString(),
                Text = x.Title
            });

e tendo em vista tbltask e projectlist são meus dois modelos diff

@ {

IEnumerable<SelectListItem> plist = ViewBag.projectList;

} @model List

Muhafil Saiyed
fonte