无法在MvcSiteMap视图中访问ViewData

时间:2014-05-26 14:25:53

标签: asp.net-mvc viewdata mvcsitemapprovider asp.net-mvc-5.1

我正在构建一个非常大的MVC项目,我的查看数据有些麻烦。我有一个非常复杂的模型结构。我不想要“反对”#39;我的模型有简单的显示数据,但我想要强类型数据,所以我写了几个简单的类:

public class GeneralViewData
{
    private static string Id = "GeneralViewData";

    public string PageTitle { get; set; }
    public bool ShowGuestMenu { get; set; }
    public string CurrentUserName { get; set; }
    public bool ShowOrganisationAdminMenu { get; set; }

    protected GeneralViewData() { }

    public static GeneralViewData Retrieve(ViewDataDictionary viewDataDictionary)
    {
        object viewData = viewDataDictionary[Id];
        if (viewData == null) return Initiate(viewDataDictionary);
        return (GeneralViewData)viewData;
    }

    private static GeneralViewData Initiate(ViewDataDictionary viewDataDictionary)
    {
        GeneralViewData viewData = new GeneralViewData();
        viewDataDictionary[Id] = viewData;
        return viewData;
    }
}

因此,只要您可以访问ViewData,就可以轻松获取模型并通过以下方式访问它:

var generalViewData = GeneralViewData.Retrieve(ViewData);

这适用于大多数网站,但我遇到了MVCSiteMapProvider视图的问题。显然,sitemapprovrov不会传递主ViewData,而是将新的ViewData传递给部分视图。

我需要一种方法让MVCSiteMapProvider将完整的ViewData传递给它的视图,或者通过在其他地方存储GeneralViewData实例来完全解决这个问题。我一直在绞尽脑汁想要解决这个问题,所以我可以做任何事情。

1 个答案:

答案 0 :(得分:1)

选项#1

您可以使用SiteMapTitleAttribute,但由于您的节点是菜单的一部分,因此您必须在每个控制器操作方法中执行此操作。

选项#2

要使其在全局工作而不将SiteMapTitleAttribute添加到每个控制器操作,您还可以在Application_PostAuthorizeRequest事件中显式设置标题。

protected void Application_PostAuthorizeRequest(object sender, EventArgs eventArgs)
{
    var user = HttpContext.Current.User;
    if (user.Identity.IsAuthenticated)
    {
        var accountNode = MvcSiteMapProvider.SiteMaps.Current.FindSiteMapNodeFromKey("MyAccount");
        if (accountNode != null)
        {
            accountNode.Title = "Hello, " + user.Identity.Name;
        }
    }
}

这假设您的帐户节点配置了一个显式密钥集,其值为“MyAccount”。

选项#3

更简洁的方法可能是使用custom attribute添加可以检索当前用户名和其他属性的类。

如果在上面的Application_PostAuthorizeRequest事件中设置对象,则可能会将现有对象用于静态属性。

但是,更集成的方法是使用[IDynamicNodeProvider]或ISiteMapNodeProvider来提供自定义对象。请注意,如果您使用此路由,则需要使用具有从当前请求检索数据的属性/方法的对象(而不是存储它们),因为此对象将在SiteMap的所有用户之间共享。

然后,您可以自定义MvcSiteMapProvider显示模板,以便在自定义对象存在时在适当的节点上显示此数据。

选项#4

按照IPrincipal所述实施IIdentity和/或here,更好地将模型与ASP.NET集成,然后将您的个人资料数据添加到自定义IPrincipal和/或IIdentity实施中所以它可以通过HttpContext对象全局使用。

数据全局可用后,您可以直接从您的视图中访问它。然后,您可以自定义SiteMapNodeModel显示模板,以便在适当时显示数据。

@model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models

@* Fix the title for the MyAccount Node *@
var title = Model.Title;
if (Model.Key == "MyAccount")
{
    title = "Hello, " + (User as CustomPrincipal).CurrentUserName;
}

@if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper")  { 
    <text>@title</text>
} else if (Model.IsClickable) {
    if (string.IsNullOrEmpty(Model.Description))
    {
        <a href="@Model.Url">@title</a>
    }
    else
    {
        <a href="@Model.Url" title="@Model.Description">@title</a>
    }
} else { 
    <text>@title</text>
}

此示例假定您已明确将帐户节点的键属性/属性设置为“MyAccount”。

我还建议您不要使用“ShowGuestMenu”和“ShowOrganisationAdminMenu”的布尔值,而是为此创建一个ASP.NET角色,并使用AuthorizeAttribute与Security Trimming一起控制子的可见性菜单。如果您希望用户能够打开和关闭菜单,我建议您按照自己的方式进行操作,在这种情况下,我建议您使用custom visibility provider而不是通过更改显示模板来控制可见性。