Passe dados para o layout comum a todas as páginas


124

Eu tenho um site que tem uma página de layout. No entanto, esta página de layout possui dados que todos os modelos de páginas devem fornecer como título, nome da página e o local em que realmente estamos para um auxiliar de HTML que fiz, que executa alguma ação. Além disso, cada página tem suas próprias propriedades de modelos de exibição.

Como posso fazer isso? Parece que é uma má idéia digitar um layout, mas como passo essas informações?


10
Para quem ler as respostas aqui, consulte stackoverflow.com/a/21130867/706346, onde você verá uma solução muito mais simples e organizada que qualquer coisa postada aqui.
Avrohom Yisroel

5
@AvrohomYisroel boa sugestão. No entanto, prefiro a abordagem do @Colin Bacon porque é forte e tipificada e não no ViewBag. Talvez uma questão de preferências. Voto positivo com o seu comentário embora
JP Hellemons

Para o mvc 5, veja esta resposta: stackoverflow.com/a/46783375/5519026
Laz Ziya

Respostas:


142

Se você precisar passar as mesmas propriedades para cada página, seria sensato criar um modelo de vista base usado por todos os seus modelos de vista. Sua página de layout pode usar esse modelo base.

Se houver lógica necessária por trás desses dados, isso deve ser colocado em um controlador de base usado por todos os seus controladores.

Você pode fazer muitas coisas, a abordagem importante é não repetir o mesmo código em vários lugares.

Editar: Atualização dos comentários abaixo

Aqui está um exemplo simples para demonstrar o conceito.

Crie um modelo de vista base do qual todos os modelos herdarão.

public abstract class ViewModelBase
{
    public string Name { get; set; }
}

public class HomeViewModel : ViewModelBase
{
}

Sua página de layout pode aceitar isso como modelo.

@model ViewModelBase
<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Test</title>
    </head>
    <body>
        <header>
            Hello @Model.Name
        </header>
        <div>
            @this.RenderBody()
        </div>
    </body>
</html>

Por fim, defina os dados no método de ação.

public class HomeController
{
    public ActionResult Index()
    {
        return this.View(new HomeViewModel { Name = "Bacon" });
    }
}

12
Mas os dados são usados ​​no layout. Como posso passar os dados para o layout?
Rushino

2
Perfeito! Eu vi meu erro. Eu esqueci de passar o modelo para a vista .. que erro lame. Obrigado!
Rushino 5/11

7
o problema dessa abordagem é que, às vezes, nem todas as visualizações têm um ViewModel; portanto, isso não funciona nesse caso: O /
Cacho Santa

16
Mas isso não exigiria que todo controlador e toda ação incluísse o código {Name = "Bacon"}? E se eu quisesse adicionar outra propriedade ao ViewModelBase, teria que ir para todos os controladores e ações e adicionar o código para preencher essa propriedade? Você mencionou "Se houver lógica necessária, [...] isso deve ser colocado em um controlador básico [...]". Como isso funcionaria para eliminar esse código repetido em todos os controladores e ações?
Lee

5
@ Lee Se houver dados comuns em todas as páginas, um controlador básico é onde você colocaria isso. Seus controladores então herdam deste controlador base. por exemplo public class HomeController : BaseController. Dessa forma, o código comum precisa ser gravado apenas uma vez e pode ser aplicado a todos os controladores.
Colin Bacon

73

Eu usei o RenderAction html helper para fazer a navalha no layout.

@{
   Html.RenderAction("Action", "Controller");
 }

Eu precisava disso para uma string simples. Portanto, minha ação retorna uma string e a anota com facilidade. Mas se você precisar de dados complexos, poderá retornar o PartialViewResult e model.

 public PartialViewResult Action()
    {
        var model = someList;
        return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
    }

Você só precisa colocar o modelo no início da exibição parcial '_maPartialView.cshtml' que você criou

@model List<WhatEverYourObjeIs>

Em seguida, você pode usar os dados no modelo nessa vista parcial com html.


18
Esta é de longe a melhor resposta!
gingerbreadboy

@gingerbreadboy concordou que promove um bom encapsulamento e uma separação de preocupações.
A-Dubb

35

Outra opção é criar uma classe LayoutModel separada com todas as propriedades necessárias no layout e inserir uma instância dessa classe no ViewBag. Eu uso o método Controller.OnActionExecuting para preenchê-lo. Em seguida, no início do layout, você pode retirar esse objeto do ViewBag e continuar acessando esse objeto fortemente digitado.


1
Isso realmente parece a solução menos dolorosa, existem desvantagens? 1
formatc:

2
Definitivamente, a melhor solução e não vejo desvantagens.
Wiktor Zychla

7
Não vejo o que isso está lhe dando. Se você tem uma classe com todas as propriedades necessárias para o layout, por que se preocupar em adicioná-la ao ViewBag apenas para ter que devolvê-la novamente? Use o modelo na exibição de layout. Ainda é possível preencher o modelo OnActionExecuting. Usar o ViewBag também significa que você perde a segurança do tipo no seu controlador, nunca é uma coisa boa.
Colin Bacon

3
O que isso me dá é a capacidade de adicionar um modelo para layout, sem ter que reestruturar todos os modelos para herdar do modelo "super" único em todos os métodos de todos os controladores, em um projeto que já existe. Se você está começando do zero, pode optar por derivar todos os seus modelos da raiz comum.
DenNukem 23/09/2015

5
O @ColinBacon outra vantagem dessa opção é que suas ações nem sempre precisam ter modelos de visualização. Além disso, eu argumentaria que os desenvolvedores que precisam saber que sempre devem herdar seus modelos de exibição de uma base são uma desvantagem.
Josh Noe #

28

Presumivelmente, o principal caso de uso para isso é obter um modelo base para a visualização de todas (ou a maioria das) ações do controlador.

Dado isso, usei uma combinação de várias dessas respostas, o principal apoio da resposta de Colin Bacon.

É correto que isso ainda seja lógico do controlador, porque estamos preenchendo um modelo de visão para retornar a uma visão. Portanto, o local correto para colocar isso é no controlador.

Queremos que isso aconteça em todos os controladores, porque usamos isso para a página de layout. Estou usando-o para exibições parciais que são renderizadas na página de layout.

Também queremos o benefício adicional de um ViewModel fortemente tipado

Assim, eu criei um BaseViewModel e BaseController. Todos os controladores ViewModels herdarão do BaseViewModel e BaseController, respectivamente.

O código:

BaseController

public class BaseController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var model = filterContext.Controller.ViewData.Model as BaseViewModel;

        model.AwesomeModelProperty = "Awesome Property Value";
        model.FooterModel = this.getFooterModel();
    }

    protected FooterModel getFooterModel()
    {
        FooterModel model = new FooterModel();
        model.FooterModelProperty = "OMG Becky!!! Another Awesome Property!";
    }
}

Observe o uso de OnActionExecuted, conforme extraído desta postagem do SO

Página inicial

public class HomeController : BaseController
{
    public ActionResult Index(string id)
    {
        HomeIndexModel model = new HomeIndexModel();

        // populate HomeIndexModel ...

        return View(model);
    }
}

BaseViewModel

public class BaseViewModel
{
    public string AwesomeModelProperty { get; set; }
    public FooterModel FooterModel { get; set; }
}

HomeViewModel

public class HomeIndexModel : BaseViewModel
{

    public string FirstName { get; set; }

    // other awesome properties
}

FooterModel

public class FooterModel
{
    public string FooterModelProperty { get; set; }
}

Layout.cshtml

@model WebSite.Models.BaseViewModel
<!DOCTYPE html>
<html>
<head>
    < ... meta tags and styles and whatnot ... >
</head>
<body>
    <header>
        @{ Html.RenderPartial("_Nav", Model.FooterModel.FooterModelProperty);}
    </header>

    <main>
        <div class="container">
            @RenderBody()
        </div>

        @{ Html.RenderPartial("_AnotherPartial", Model); }
        @{ Html.RenderPartial("_Contact"); }
    </main>

    <footer>
        @{ Html.RenderPartial("_Footer", Model.FooterModel); }
    </footer>

    < ... render scripts ... >

    @RenderSection("scripts", required: false)
</body>
</html>

_Nav.cshtml

@model string
<nav>
    <ul>
        <li>
            <a href="@Model" target="_blank">Mind Blown!</a>
        </li>
    </ul>
</nav>

Espero que isso ajude.


2
Usei essa abordagem, mas prefiro herdar de uma interface do que de uma classe base. Foi o que fiz: var model = filterContext.Controller.ViewData.Model como IBaseViewModel if (model! = Null) {model.AwesomeModelProperty = "Awesome Property Value"; }
Tom Gerken

2
ótima resposta, eu preferi este a todos os outros.
Jynn

1
Ótima resposta, mas eu tenho uma pergunta. "E se eu tiver algumas visualizações que não possuam ViewModels ...?"
Isma Haro

Tentei isso, mas na ação Index, OnActionExecuted preenche o FooterModel e, em seguida, um novo HomeIndexModel é criado com um nulo FooterModel :(
SteveCav

1
@drizzie: no seu controlador base, model é uma variável local no método Filter: var model = filterContext.Controller.ViewData.Model como BaseViewModel. Eu não entendo como o MVC entende que essa variável local é igual ao modelo que o HomeController está enviando para exibição.
Hooman Bahreini

9

Você não precisa mexer em ações ou alterar o modelo, basta usar um controlador de base e converter o controlador existente no contexto de visualização do layout.

Crie um controlador base com os dados comuns desejados (título / página / local etc) e inicialização de ação ...

public abstract class _BaseController:Controller {
    public Int32 MyCommonValue { get; private set; }

    protected override void OnActionExecuting(ActionExecutingContext filterContext) {

        MyCommonValue = 12345;

        base.OnActionExecuting(filterContext);
    }
}

Verifique se todos os controladores usam o controlador base ...

public class UserController:_BaseController {...

Transmitir o controlador base existente a partir do contexto de visualização em sua _Layout.cshmlpágina ...

@{
    var myController = (_BaseController)ViewContext.Controller;
}

Agora você pode consultar os valores em seu controlador base na página de layout.

@myController.MyCommonValue

ATUALIZAR

Você também pode criar uma extensão de página que permita o uso this.

//Allows typed "this.Controller()." in cshtml files
public static class MyPageExtensions {
    public static _BaseController Controller(this WebViewPage page) => Controller<_BaseController>(page);
    public static T Controller<T>(this WebViewPage page) where T : _BaseController => (T)page.ViewContext.Controller;
}

Então você só precisa se lembrar de usar this.Controller()quando quiser o controlador.

@{
    var myController = this.Controller(); //_BaseController
}

ou controlador específico que herda de _BaseController...

@{
    var myController = this.Controller<MyControllerType>();
}

Qual é o equivalente disso no núcleo .net? Como o ViewContext.Controller não está presente e há algumas alterações na cadeia de herança
Jayanth Thyagarajan

4

se você quiser passar um modelo inteiro, faça o mesmo no layout:

@model ViewAsModelBase
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="utf-8"/>
    <link href="/img/phytech_icon.ico" rel="shortcut icon" type="image/x-icon" />
    <title>@ViewBag.Title</title>
    @RenderSection("styles", required: false)    
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    @RenderSection("scripts", required: false)
    @RenderSection("head", required: false)
</head>
<body>
    @Html.Action("_Header","Controller", new {model = Model})
    <section id="content">
        @RenderBody()
    </section>      
    @RenderSection("footer", required: false)
</body>
</html>

e adicione isso no controlador:

public ActionResult _Header(ViewAsModelBase model)

4

Eu não acho que nenhuma dessas respostas seja flexível o suficiente para um aplicativo de nível corporativo grande. Não sou fã de usar demais o ViewBag, mas, neste caso, por flexibilidade, eu abriria uma exceção. Aqui está o que eu faria ...

Você deve ter um controlador de base em todos os seus controladores. Adicione seus dados de layout OnActionExecuting no seu controlador base (ou OnActionExecuted se desejar adiar isso) ...

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext     
        filterContext)
    {
        ViewBag.LayoutViewModel = MyLayoutViewModel;
    }
}

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View(homeModel);
    }
}

Em seguida, no seu _Layout.cshtml, puxe seu ViewModel do ViewBag ...

@{
  LayoutViewModel model = (LayoutViewModel)ViewBag.LayoutViewModel;
}

<h1>@model.Title</h1>

Ou...

<h1>@ViewBag.LayoutViewModel.Title</h1>

Isso não interfere na codificação dos controladores da sua página ou nos modelos de exibição.


Eu gosto da sua ideia, mas e se você tiver MyLayoutViewModelcriado dinamicamente, como posso passar alguns parâmetros para o OnActionExecutingmétodo?
Rajmond Burgaj

1
Você ainda precisa base.OnActionExecuting(filterContext)no seu OnActionExecutingmétodo !!!
ErikE

4

Criar uma vista de base que representa o modelo de vista de layout é uma abordagem terrível. Imagine que você deseja ter um modelo que represente a navegação definida no layout. Você fariaCustomersViewModel : LayoutNavigationViewModel ? Por quê? Por que você deve passar os dados do modelo de navegação por todos os modelos de exibição existentes na solução?

O modelo de visualização de layout deve ser dedicado, por si só, e não deve forçar o restante dos modelos de visualização a depender dele.

Em vez disso, você pode fazer isso no seu _Layout.cshtmlarquivo:

@{ var model = DependencyResolver.Current.GetService<MyNamespace.LayoutViewModel>(); }

Mais importante ainda, não precisamos new LayoutViewModel()e teremos todas as dependências que LayoutViewModelforam resolvidas para nós.

por exemplo

public class LayoutViewModel
{
    private readonly DataContext dataContext;
    private readonly ApplicationUserManager userManager;

    public LayoutViewModel(DataContext dataContext, ApplicationUserManager userManager)
    {
    }
}

Onde você preenche este modelo? Em um BaseController também?
Ndberg 21/11

Eu imagino que isso seria uma boa idéia para um Scopedobjeto de modelo de layout no ASP..Net Core também.
James Wilkins

Eu não teria a visão buscar uma dependência. Definitivamente, isso não é "MVC". O Localizador de serviço é um antipadrão .
Jiveman

Ao contrário da crença popular, o Service Locator não é um antipadrão e, na verdade, isso não tem nada a ver com o MVC, você está apenas jogando palavras exageradas @Jiveman? blog.gauffin.org/2012/09/service-locator-is-not-an-anti-pattern
hyankov

O ponto principal de Jgauffin nesse artigo parece ser que o termo "antipadrão" não deve ser aplicado ao Service Locator, pois pode haver pelo menos alguns usos válidos do SL. Um ponto justo. No entanto, como é evidente em alguns de seus próprios comentários de discussão, ele sugere que, embora o SL possa ser uma abordagem válida ao criar bibliotecas e estruturas, não é necessariamente recomendado ao criar aplicativos (que eu consideraria a pergunta do OP e essa discussão aqui girando em torno de).
Jiveman

3

Outras respostas cobriram praticamente tudo sobre como podemos passar o modelo para a nossa página de layout. Mas eu encontrei uma maneira pela qual você pode passar variáveis ​​para sua página de layout dinamicamente sem usar nenhum modelo ou exibição parcial em seu layout. Digamos que você tenha esse modelo -

public class SubLocationsViewModel
{
    public string city { get; set; }
    public string state { get; set; }
}

E você deseja obter cidade e estado dinamicamente. Por exemplo

no seu index.cshtml, você pode colocar essas duas variáveis ​​no ViewBag

@model  MyProject.Models.ViewModel.SubLocationsViewModel
@{
    ViewBag.City = Model.city;
    ViewBag.State = Model.state;
}

E então, em seu layout.cshtml, você pode acessar essas variáveis ​​do viewbag

<div class="text-wrap">
    <div class="heading">@ViewBag.City @ViewBag.State</div>
</div>

isso funciona muito bem, @stun_Gravy existe uma queda usando o ViewBag para transmitir dados como função ou nível de acesso do usuário?
3not3

3

Há outra maneira de lidar com isso. Talvez não seja o caminho mais limpo do ponto de vista arquitetônico, mas evita muita dor envolvida nas outras respostas. Simplesmente injete um serviço no layout do Razor e chame um método que obtenha os dados necessários:

@inject IService myService

Posteriormente, na exibição de layout:

@if (await myService.GetBoolValue()) {
   // Good to go...
}

Novamente, não é limpo em termos de arquitetura (obviamente o serviço não deve ser injetado diretamente na visualização), mas faz o trabalho.


Não é a maneira mais limpa? Discordo. Eu acho que é o mais limpo possível: o objeto é passado do local em que é criado diretamente para o local em que você deseja estar, sem "poluir" outros controladores com itens que eles não precisam ver. Usar @injecté a melhor solução, na minha opinião.
dasblinkenlight

1
Pensando nisso mais, talvez você esteja certo. O fato de esse método evitar tanta dor é um sinal de que talvez seja o caminho mais limpo. Eu tenho trabalhado em um aplicativo ASP.NET Core muito grande e estou usando esse padrão para coisas como lógica de navegação de navegação, dados de cabeçalho que estão na maioria das páginas, coisas assim. Evitei muita dor fazendo desta maneira.
Andrew

2

Você também pode fazer uso do RenderSection ; isso ajuda a injetar seus Modeldados no_Layout visualização.

Você pode injetar View ModelDados, Json, Script, CSS,HTML etc

Neste exemplo, estou injetando Jsondo meu IndexView paraLayout View.

Index.chtml

@section commonLayoutData{

    <script>

        var products = @Html.Raw(Json.Encode(Model.ToList()));

    </script>

    }

_Layout.cshtml

@RenderSection("commonLayoutData", false)

Isso elimina a necessidade de criar uma base separada View Model .

A esperança ajuda alguém.


1
Solução perfeita quando você precisa renderizar algo específico para poucas visualizações.
Kunal

1

o que eu fiz é muito simples e funciona

Declare a propriedade Static em qualquer controlador ou você pode criar uma classe de dados com valores estáticos, se desejar:

public static username = "Admin";
public static UserType = "Administrator";

Esses valores podem ser atualizados pelos controladores com base nas operações. depois, você pode usá-los em seu _Layout

Em _layout.cshtml

@project_name.Controllers.HomeController.username
@project_name.Controllers.HomeController.UserType

1
É sempre útil adicionar alguma explicação à sua resposta, para torná-la mais clara e compreensível. Leia stackoverflow.com/help/how-to-answer .
32cupo

0

Por que ninguém sugeriu métodos de extensão no ViewData?

Opção 1

Parece-me de longe a solução menos intrusiva e mais simples para o problema. Sem seqüências codificadas. Sem restrições impostas. Sem codificação mágica. Nenhum código complexo.

public static class ViewDataExtensions
{
    private const string TitleData = "Title";
    public static void SetTitle<T>(this ViewDataDictionary<T> viewData, string value) => viewData[TitleData] = value;
    public static string GetTitle<T>(this ViewDataDictionary<T> viewData) => (string)viewData[TitleData] ?? "";
}

Definir dados na página

ViewData.SetTitle("abc");

Opção 2

Outra opção, facilitando a declaração de campo.

public static class ViewDataExtensions
{
    public static ViewDataField<string, V> Title<V>(this ViewDataDictionary<V> viewData) => new ViewDataField<string, V>(viewData, "Title", "");
}

public class ViewDataField<T,V>
{
    private readonly ViewDataDictionary<V> _viewData;
    private readonly string _field;
    private readonly T _defaultValue;

    public ViewDataField(ViewDataDictionary<V> viewData, string field, T defaultValue)
    {
        _viewData = viewData;
        _field = field;
        _defaultValue = defaultValue;
    }

    public T Value {
        get => (T)(_viewData[_field] ?? _defaultValue);
        set => _viewData[_field] = value;
    }
}

Defina os dados na página. A declaração é mais fácil que a primeira opção, mas a sintaxe de uso é um pouco mais longa.

ViewData.Title().Value = "abc";

Opção # 3

Em seguida, você pode combinar isso com o retorno de um único objeto que contém todos os campos relacionados ao layout com seus valores padrão.

public static class ViewDataExtensions
{
    private const string LayoutField = "Layout";
    public static LayoutData Layout<T>(this ViewDataDictionary<T> viewData) => 
        (LayoutData)(viewData[LayoutField] ?? (viewData[LayoutField] = new LayoutData()));
}

public class LayoutData
{
    public string Title { get; set; } = "";
}

Definir dados na página

var layout = ViewData.Layout();
layout.Title = "abc";

Esta terceira opção tem vários benefícios e acho que é a melhor opção na maioria dos casos:

  • Declaração mais simples de campos e valores padrão.

  • Sintaxe de uso mais simples ao definir vários campos.

  • Permite definir vários tipos de dados no ViewData (por exemplo, Layout, Cabeçalho, Navegação).

  • Permite código e lógica adicionais dentro da classe LayoutData.

PS Não se esqueça de adicionar o namespace de ViewDataExtensions em _ViewImports.cshtml


0

Você pode criar um arquivo navalha na pasta App_Code e acessá-lo nas suas páginas de exibição.

Projeto> Repository / IdentityRepository.cs

namespace Infrastructure.Repository
{
    public class IdentityRepository : IIdentityRepository
    {
        private readonly ISystemSettings _systemSettings;
        private readonly ISessionDataManager _sessionDataManager;

        public IdentityRepository(
            ISystemSettings systemSettings
            )
        {
            _systemSettings = systemSettings;
        }

        public string GetCurrentUserName()
        {
            return HttpContext.Current.User.Identity.Name;
        }
    }
}

Projeto> App_Code / IdentityRepositoryViewFunctions.cshtml:

@using System.Web.Mvc
@using Infrastructure.Repository
@functions
{
    public static IIdentityRepository IdentityRepositoryInstance
    {
        get { return DependencyResolver.Current.GetService<IIdentityRepository>(); }
    }

    public static string GetCurrentUserName
    {
        get
        {
            var identityRepo = IdentityRepositoryInstance;
            if (identityRepo != null)
            {
                return identityRepo.GetCurrentUserName();
            }
            return null;
        }
    }
}

Projeto> Visualizações / Compartilhado / _Layout.cshtml (ou qualquer outro arquivo .cshtml)

<div>
    @IdentityRepositoryViewFunctions.GetCurrentUserName
</div>

-1

em vez de passar por isso, você sempre pode usar outra abordagem que também é rápida

crie uma nova exibição parcial no Diretório compartilhado e chame sua exibição parcial em seu layout como

@Html.Partial("MyPartialView")

na sua visão parcial, você pode chamar seu banco de dados e executar o que quiser

@{
    IEnumerable<HOXAT.Models.CourseCategory> categories = new HOXAT.Models.HOXATEntities().CourseCategories;
}

<div>
//do what ever here
</div>

supondo que você adicionou seu banco de dados do Entity Framework


1
Downcasting, pois nunca deve ser responsabilidade da View obter seu próprio Modelo.
Oxonhammer

-1

É incrível que ninguém tenha dito isso aqui. Passar um viewmodel através de um controlador base é uma bagunça. Estamos usando reivindicações do usuário para passar informações para a página de layout (para mostrar dados do usuário na barra de navegação, por exemplo). Há mais uma vantagem. Os dados são armazenados por meio de cookies, portanto, não há necessidade de recuperar os dados em cada solicitação por meio de parciais. Basta fazer algumas "reivindicações de identidade de rede asp" no Google.


@CodeSmith what? Estou fornecendo uma solução.
makore

Meu mal, pensei que era uma pergunta, mas veja que é uma resposta agora, excluída.
CodeSmith

Se for uma tentativa de responder à pergunta, ela deve ser esclarecida. O comentário é tolerado, mas não deve estar dominando a resposta inteira; Além disso, dicas sobre o que pesquisar no Google não constituem uma resposta válida.
Tripleee

-6

Você pode usar assim:

 @{ 
    ApplicationDbContext db = new ApplicationDbContext();
    IEnumerable<YourModel> bd_recent = db.YourModel.Where(m => m.Pin == true).OrderByDescending(m=>m.ID).Select(m => m);
}
<div class="col-md-12">
    <div class="panel panel-default">
        <div class="panel-body">
            <div class="baner1">
                <h3 class="bb-hred">Recent Posts</h3>
                @foreach(var item in bd_recent)
                {
                    <a href="/BaiDangs/BaiDangChiTiet/@item.ID">@item.Name</a>
                }
            </div>
        </div>
    </div>
</div>

7
Conectar-se ao banco de dados em vista é uma péssima idéia.
1_bug
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.