【问题标题】:Build a Layout ViewModel based in a Id from the URL with MVC使用 MVC 从 URL 构建基于 Id 的布局视图模型
【发布时间】:2020-01-22 16:48:37
【问题描述】:

我需要为我的 Layout 的 Web 应用程序构建一个 ViewModel,我已经尝试过 this solution,但它不是基于来自 URL 的 URL 的 Id 来生成 Layout 的 ViewModel。

我尝试了这个,但我首先遇到了不存在的空控制器错误,然后我尝试将 id 作为参数包含在内,但我收到错误“对象引用未设置为对象的实例”。因为没有设置 LayoutColorRGB。

public MobileController(int id)
{
    Event model = db.Events.Where(s => s.Id == id).FirstOrDefault();

    LayoutVM = new LayoutVM()
    {
        EventId = model.Id,
        LayoutColorRGB = model.LayoutColorRGB,
        SponsorLogoLink = model.SponsorLogoLink,
        SponsorLogoURL = model.SponsorLogoURL
    };

    ViewData["LayoutVM"] = LayoutVM;
}

【问题讨论】:

  • 你为什么要在你的构造函数上这样做?
  • 这是基于stackoverflow.com/a/14882183/1480877的解决方案
  • 我发布了一个不依赖于外部类的示例,以使事情变得简单且易于验证。

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


【解决方案1】:

有很多情况需要根据请求上下文提取数据并在布局页面上显示一些东西。例如:

  • 您可能希望显示登录用户信息
  • 您可能希望显示访问者或在线访问者的数量
  • 您可能希望显示当前语言并让用户更改语言。
  • 您可能希望从数据库加载站点菜单或侧边栏或页脚

为此,您可以考虑以下几点:

  • 局部视图:您可以为每个局部视图具有特定模型的那些部分创建一些小的局部视图,并将它们呈现在布局页面中。

  • 使用上下文获取数据:您可以通过从RequestViewContextRouteDataValueProvider和其他上下文对象中提取信息来初始化模型。 p>

  • 通过 HTML 助手访问数据:您可以创建 HtmlHelper 以从上下文中获取数据并在布局或部分视图中使用助手。

  • 通过依赖注入访问数据:您可以定义一些服务来提取数据,然后将这些数据注入到布局页面。在服务中,您将使用上下文对象初始化模型。如果您使用的是 ASP.NET CORE,这是一个不错的选择。

  • 作为基本控制器的属性访问数据:您可以在基本控制器中拥有一个属性,并在控制器的构造函数或OnActionExecuting 中对其进行初始化。然后在布局中,通过将ViewContext.Controller 转换为基本控制器的类型来获取属性并读取属性。

  • ViewBag 访问数据:可以在基控制器的构造函数中或基控制器的OnActionExecuting 方法中初始化模型实例,然后放入ViewBag。然后就可以在view中轻松使用了。

  • 布局页面:不要忘记您可以根据需要定义不同的布局页面并使用不同的布局。您可以在操作中或_ViewStart 中设置布局。

示例

尝试在每个请求中解析 id,这意味着您需要将 id 作为所有请求的一部分,或者您需要知道在没有 id 时应该做什么。考虑到这一事实并为一个最小的示例保持简单,我将定义以下模型和基本控制器,并尝试在基本控制器的 OnActionExecuting 方法中解析 id,然后将派生我所有需要这种行为的控制器基础控制器。

您可以使用ActionFilter 或控制器的OnActionExecuting 方法的全局操作过滤器来执行相同操作。

使用以下代码:

  • 如果您浏览/home/index,您将在页面底部看到一个红色条。
  • 如果您浏览/home/index/1,您将在页面底部看到一个蓝色条
  • 如果您浏览/home/index/2,您会在页面底部看到一个绿色条

布局模型

public class LayoutViewModel
{
    public int? Id { get; set; }
    public string Color { get; set; }
}

基本控制器

public class BaseControllr : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //Get the id from route
        var id = int.TryParse(ValueProvider.GetValue("id")?.AttemptedValue, out var temp)
            ? temp : default(int?);
        var model = new LayoutViewModel();

        //Your logic to initialize the model, for example
        model.Id = id;
        if (model.Id == null)
            model.Color = "FF0000";
         else if (model.Id%2==0)
            model.Color = "00FF00";
        else
            model.Color = "0000FF";

        //Set ViewBag
        ViewBag.MainLayoutViewModel = model;
        base.OnActionExecuting(filterContext);
    }
}

家庭控制器

public class HomeController : BaseControllr
{
    public ActionResult Index(int? id)
    {
        return View();
    }
} 

_Layout.cshtml

然后在_Layout.cshtml中,在关闭<body/>标签之前添加如下代码进行测试:

@{ 
    string color = ViewBag.MainLayoutViewModel?.Color;
    int? id = ViewBag.MainLayoutViewModel?.Id;
}
<div style="background-color:#@color;">
    Id:@id
</div>

【讨论】:

  • 您总是需要一些通用数据才能在 LayoutPages 上使用。例如,您要显示登录用户信息,要显示访问者或在线访问者的数量,要显示当前语言并让用户更改语言。所以这个要求是有道理的。
  • 有几种解决方案可以满足这一要求。例如,您可以使用操作过滤器或基本控制器从请求中提取一些数据并放入ViewBag。您可以在没有ViewBag 的情况下通过在基本控制器中使用Property 来执行相同操作,并通过将ViewContext.Controller 转换为基本控制器的类型并读取属性来获取属性。您可以创建HtmlHelper 以从上下文中获取数据。您可以依靠依赖注入并从服务和更多选项中获取所需的数据。
  • @Patrick 我相信答案涵盖了很多问题,并包含一个示例,说明如何处理此案。添加更多示例 IMO 超出了帖子的范围。如果您对该案例有更多疑问,或者您认为答案有用,请告诉我。
  • @Patrick 这取决于。如果您要求在所有请求中显示事件,一般都可以;但是在某些情况下,您可能希望使用缓存机制。但总的来说不要担心,请再次阅读我的第二条评论或我的答案的第一段。希望对你有帮助?
  • 不客气?,所以如果数据像颜色或其他不太可能经常变化的皮肤/布局相关数据,你可以为它们使用缓存,所以你不要不需要在来自数据库的每个请求中获取它们。您可以基于时间使缓存失效,甚至在数据更改时使缓存失效。
【解决方案2】:

您应该为布局的每个属性设置一个默认值。然后,如果您的模型没有某些属性,您可以从默认布局中使用它。

internal static readonly LayoutVM defaultLayout = new LayoutVM()
{
    EventId = 0,
    LayoutColorRGB = "#FFFFFF",
    SponsorLogoLink = "AnyLink",
    SponsorLogoURL = "AnyImageUrl"
};

public MobileController(int id)
{

    Event model = db.Events.Where(s => s.Id == id).FirstOrDefault();

    if (model == null)
    {
        ViewData["LayoutVM"] = defaultLayout;
        return;
    }

    LayoutVM = new LayoutVM()
    {
        EventId = model.Id,
        LayoutColorRGB = model.LayoutColorRGB ?? defaultLayout.LayoutColorRGB,
        SponsorLogoLink = model.SponsorLogoLink ?? defaultLayout.SponsorLogoLink,
        SponsorLogoURL = model.SponsorLogoURL ?? defaultLayout.SponsorLogoURL
    };

    ViewData["LayoutVM"] = LayoutVM;
}

【讨论】:

  • 您好,谢谢!但我无法编译它,它会生成很多错误“名称 defaultLayout 在当前上下文中不存在”、“修饰符 new 对此项目无效”等。
  • 对于错误当前上下文中不存在名称defaultLayout将私有静态更改为内部静态。
  • 对于第二个错误,将我的答案的第一行更改为: internal static readonly LayoutVM defaultLayout = new LayoutVM()
  • 如果您遇到任何其他错误,请使用您的控制器的完整源代码更新您的问题。这样我可以更容易地找到错误。没有它我只是猜测
  • 感谢 Lutti 的帮助,我已经实施了@Reza 的解决方案,它运行良好。我试过你的,但我无法让它运行,我试了几次,有不同的选择,但我总是出错。
猜你喜欢
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-04
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多