【问题标题】:ASP.NET MVC Razor pass model to layoutASP.NET MVC Razor 将模型传递给布局
【发布时间】:2011-05-08 10:51:24
【问题描述】:

我看到的是一个字符串布局属性。但是如何将模型显式传递给布局?

【问题讨论】:

  • 我有几个不同型号但布局相同的页面
  • 这个 stackoverflow 问题似乎回答了你的问题:stackoverflow.com/questions/13225315/…
  • 我没有这个问题。 Model_Layout 中可用。我正在使用 MVC5。

标签: c# .net asp.net-mvc razor


【解决方案1】:
  1. 向您的控制器(或基本控制器)添加一个名为 MainLayoutViewModel(或其他)的属性,使用您想要使用的任何类型。
  2. 在控制器(或基本控制器)的构造函数中,实例化类型并将其设置为属性。
  3. 将其设置为 ViewData 字段(或 ViewBag)
  4. 在“布局”页面中,将该属性转换为您的类型。

示例: 控制器:

public class MyController : Controller
{
    public MainLayoutViewModel MainLayoutViewModel { get; set; }

    public MyController()
    {
        this.MainLayoutViewModel = new MainLayoutViewModel();//has property PageTitle
        this.MainLayoutViewModel.PageTitle = "my title";

        this.ViewData["MainLayoutViewModel"] = this.MainLayoutViewModel;
    }

}

布局页面顶部示例

@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}

现在您可以在布局页面中引用变量“viewModel”,并拥有对类型化对象的完全访问权限。

我喜欢这种方法,因为它是控制布局的控制器,而各个页面视图模型与布局无关。

MVC 核心注意事项


在第一次调用每个操作时,Mvc Core 似乎会清除 ViewData/ViewBag 的内容。这意味着在构造函数中分配 ViewData 不起作用。然而,有效的是使用IActionFilter 并在OnActionExecuting 中执行完全相同的工作。将MyActionFilter 放在您的MyController 上。
public class MyActionFilter: Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            var myController= context.Controller as MyController;

            if (myController!= null)
            {
                myController.Layout = new MainLayoutViewModel
                {

                };

                myController.ViewBag.MainLayoutViewModel= myController.Layout;
            }
        }
    }

【讨论】:

  • 我明白了...但是动态/演员表对于剃须刀页面来说非常重要。您可以做的一件事是向 MainLayoutViewModel 添加一个静态方法,该方法为您执行转换(例如 MainLayoutViewModel.FromViewBag(this.ViewBag)),因此至少转换发生在一个地方,您可以更好地处理那里的异常。
  • @BlackjacketMack 很好的方法,我使用上面的方法实现了它并进行了一些修改,因为我有一个差异要求,这对我很有帮助,谢谢。如果是的话,我们可以使用 TempData 来达到同样的效果,那么如何和否,那么请告诉我为什么不能使用它。再次感谢。
  • @User - TempData 使用 Session 并且总是让我觉得有点笨拙。我的理解是它是“只读一次”,因此一旦您阅读它,它就会将其从会话中删除(或者可能在请求结束后立即删除)。您可能将会话存储在 Sql Server(或 Dynamo Db)中,因此请考虑您必须序列化 MasterLayoutViewModel 的事实……而不是您最想要的。所以基本上,将其设置为 ViewData 将其存储在内存中的一个小灵活字典中,这符合要求。
  • 很简单,我使用了您的解决方案,但是,我是 MVC 的新手,所以我只是想知道这是否被认为是一种好习惯?或者至少还不错?
  • 嗨,Karim AG,我认为两者兼而有之。我倾向于认为将内容存储在 ViewData 中是一种不好的做法(很难跟踪,基于字典,不是真正的类型)......但是......在强类型对象中输入所有布局属性是一种很好的做法。所以我妥协说,好吧,让我们在里面存储一个东西,但把它的其余部分锁定到一个好的强类型 ViewModel。
【解决方案2】:

如果您遇到此问题,似乎您对视图模型的建模有点错误。

就我个人而言,我永远不会键入布局页面。但是,如果您想这样做,您应该有一个其他视图模型继承自的基本视图模型,并将您的布局输入到基本视图模型中,然后您分页到特定的一次。

【讨论】:

  • “就我个人而言,我永远不会键入布局页面。”为什么?我的意思是,您如何处理出现在所有页面中的侧面动态内容?您是否从视图中跳过控制器? / 也许你的意思是从布局中使用 RenderAction? (我现在只是在看)
  • @eglasius,我使用的解决方案因我们谈论的内容类型而异。但是一个常见的解决方案是使用 RenderAction 在布局页面中渲染需要自己的数据的部分。我不喜欢键入布局页面的原因是它会迫使您始终在所有特定视图模型中继承“基本”视图模型。以我的经验,这通常不是一个好主意,而且很多时候你会遇到问题,当更改设计为时已晚(或者需要很长时间)。
  • 如果我想通过聚合而不是继承来包含基本模型怎么办?从设计的角度来看,这是一种完全合法的方式。那我该如何处理布局呢?
  • 我有 2 个解决方案:布局的通用模型,因此我可以将 MyLayoutModel 用于视图模型,仅在布局中使用带有 MyViewModel 的 RenderPartial。或者对静态缓存部分使用 RenderAction 对动态部分使用 ajax 调用来部分呈现页面的部分。但我更喜欢第一个解决方案,因为它对搜索引擎更友好,并且很容易与 ajax 更新结合。
  • 处理已经完成的遗留代码。这是一场噩梦。不要输入你的布局......请!
【解决方案3】:

一个常见的解决方案是制作一个包含布局文件中使用的属性的基本视图模型,然后从基本模型继承到各个页面上使用的模型。

这种方法的问题在于,您现在将自己锁定在模型只能从另一个类继承的问题中,并且您的解决方案可能是您无法在您想要的模型上使用继承。

我的解决方案也是从基本视图模型开始的:

public class LayoutModel
{
    public LayoutModel(string title)
    {
        Title = title;
    }

    public string Title { get;}
}

然后我使用的是 LayoutModel 的通用版本,它继承自 LayoutModel,如下所示:

public class LayoutModel<T> : LayoutModel
{
    public LayoutModel(T pageModel, string title) : base(title)
    {
        PageModel = pageModel;
    }

    public T PageModel { get; }
}

有了这个解决方案,我就不再需要在布局模型和模型之间进行继承了。

所以现在我可以继续使用 Layout.cshtml 中的 LayoutModel,如下所示:

@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>

在页面上,您可以像这样使用通用 LayoutModel:

@model LayoutModel<Customer>
@{
    var customer = Model.PageModel;
}

<p>Customer name: @customer.Name</p>

您只需从控制器返回一个 LayoutModel 类型的模型:

public ActionResult Page()
{
    return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}

【讨论】:

  • 奖励指出多重继承问题以及如何处理它!这是可扩展性的更好答案。
  • 我认为最好的解决方案。从架构的角度来看,它是可扩展和可维护的。这是这样做的正确方法。我从不喜欢 ViewBag 或 ViewData .....它们在我看来都很老套。
【解决方案4】:

这是非常基本的东西,您需要做的就是创建一个基本视图模型并确保全部!我的意思是所有!将使用该布局的视图中的一部分将接收使用该基本模型的视图!

public class SomeViewModel : ViewModelBase
{
    public bool ImNotEmpty = true;
}

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

在_Layout.cshtml中:

@model Models.ViewModelBase
<!DOCTYPE html>
  <html>
  and so on...

在家庭控制器的Index(例如)方法中:

    public ActionResult Index()
    {
        var model = new SomeViewModel()
        {
        };
        return View(model);
    }

Index.cshtml:

@model Models.SomeViewModel

@{
  ViewBag.Title = "Title";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="row">

我不同意将模型传递给 _layout 是错误的,可以传递一些用户信息并且可以在控制器继承链中填充数据,因此只需要一个实现。

显然,为了更高级的目的,您应该考虑使用注入创建自定义静态 contaxt,并将该模型命名空间包含在 _Layout.cshtml 中。

但是对于基本用户来说,这会解决问题

【讨论】:

  • 我同意你的看法。谢谢。
  • up,只是想提一下,它也适用于接口而不是基类
【解决方案5】:

为什么不添加一个新的局部视图,使用我自己的特定控制器将所需模型传递给局部视图,最后使用 RenderPartial 或 RenderAction 在 Layout.cshtml 上渲染提到的局部视图?

我使用这种方法显示登录用户的信息,如姓名、头像等。

【讨论】:

  • 你能详细说明一下吗?我会很感激一些通过这种技术的博客文章的链接
  • 这可以工作,但为什么要降低性能?您必须等待控制器完成所有处理,返回视图,然后让用户的浏览器发出另一个请求以获取所需的数据。如果你的 Layout 依赖于数据来适当地呈现怎么办。恕我直言,这不是这个问题的答案。
【解决方案6】:

老问题,但仅提及 MVC5 开发人员的解决方案,您可以使用与视图相同的 Model 属性。

视图和布局中的 Model 属性与相同的 ViewDataDictionary 对象相关联,因此您无需做任何额外的工作即可将模型传递到布局页面,您也不必在布局中声明@model MyModelName

但是请注意,当您在布局中使用@Model.XXX 时,智能感知上下文菜单不会出现,因为这里的Model 是一个动态对象,就像ViewBag 一样。

【讨论】:

    【解决方案7】:

    也许从技术上讲这不是处理它的正确方法,但对我来说最简单和最合理的解决方案就是创建一个类并在布局中实例化它。这是其他正确方法的一次性例外。如果这比在布局中完成的更多,那么您需要认真重新考虑您在做什么,并且可能在进一步推进您的项目之前阅读更多教程。

    public class MyLayoutModel {
        public User CurrentUser {
            get {
                .. get the current user ..
            }
        }
    }
    

    然后在视图中

    @{
        // Or get if from your DI container
        var myLayoutModel = new MyLayoutModel();
    }
    

    在 .net 核心中,您甚至可以跳过它并使用依赖注入。

    @inject My.Namespace.IMyLayoutModel myLayoutModel
    

    这是一个有点阴暗的领域之一。但是考虑到我在这里看到的极其复杂的替代方案,我认为以实用性的名义做出的例外是一个很好的例外。特别是如果您确保保持简单并确保任何繁重的逻辑(我认为确实不应该有任何逻辑,但要求不同)在它所属的另一个类/层中。这肯定比为了基本上只有一个视图而污染所有控制器或模型要好..

    【讨论】:

      【解决方案8】:

      还有另一种存档方式。

      1. 只需implement BaseController class for all controllers

      2. BaseController 类中创建一个返回模型类的方法,例如。

      public MenuPageModel GetTopMenu() 
      {    
      
      var m = new MenuPageModel();    
      // populate your model here    
      return m; 
      
      }
      
      1. Layout 页面中,您可以调用该方法GetTopMenu()
      @using GJob.Controllers
      
      <header class="header-wrapper border-bottom border-secondary">
        <div class="sticky-header" id="appTopMenu">
          @{
             var menuPageModel = ((BaseController)this.ViewContext.Controller).GetTopMenu();
           }
           @Html.Partial("_TopMainMenu", menuPageModel)
        </div>
      </header>
      

      【讨论】:

        【解决方案9】:

        仅从上面,但简单的实现

        索引页面

        @model CMS.Models.IndexViewModel 
        
        @{
            ViewBag.PageModel = Model;    
        }
        

        布局页面

        @{
            var Model = (CMS.Models.IndexViewModel)ViewBag.PageModel;        
        }
        

        【讨论】:

          【解决方案10】:

          假设您的模型是对象的集合(或者可能是单个对象)。对模型中的每个对象执行以下操作。

          1) 将要显示的对象放入 ViewBag 中。例如:

            ViewBag.YourObject = yourObject;
          

          2) 在 _Layout.cshtml 的顶部添加一个 using 语句,其中包含对象的类定义。例如:

          @using YourApplication.YourClasses;

          3) 当您在 _Layout 中引用 yourObject 时,将其转换。由于您在 (2) 中所做的,您可以应用演员表。

          【讨论】:

            【解决方案11】:
            public interface IContainsMyModel
            {
                ViewModel Model { get; }
            }
            
            public class ViewModel : IContainsMyModel
            {
                public string MyProperty { set; get; }
                public ViewModel Model { get { return this; } }
            }
            
            public class Composition : IContainsMyModel
            {
                public ViewModel ViewModel { get; set; }
            }
            

            在您的布局中使用 IContainsMyModel。

            解决了。接口规则。

            【讨论】:

            • 不知道为什么你被否决了。使用与您在此处所做的类似的界面,在我的上下文中起作用。
            【解决方案12】:

            例如

            @model IList<Model.User>
            
            @{
                Layout="~/Views/Shared/SiteLayout.cshtml";
            }
            

            详细了解新的@model 指令

            【讨论】:

            • 但是如果我想将集合的第一个元素传递给 Layout 模型呢?
            • 您必须获取控制器中的第一个元素并将模型设置为@model Model.User
            • 但我希望我的页面获得 IList 和 Layout - 只有第一个元素
            • 如果我的理解正确,您希望模型成为 IList 并在视图中获取集合的第一个元素?如果是这样,请使用 @Model.First()
            • 发帖人询问如何将模型传递给 _Layout.cshtml 页面..而不是使用布局的主视图。
            猜你喜欢
            • 2021-09-30
            • 1970-01-01
            • 2020-04-24
            • 1970-01-01
            • 1970-01-01
            • 2011-06-06
            • 1970-01-01
            • 2012-06-13
            • 2023-04-05
            相关资源
            最近更新 更多