如果有人想知道我是如何解决这个问题的。
第一件事是通过这样做扩展默认的 SiteMapNode(2 个更改是添加 ToLower()):
public class CustomSiteMapNode : RequestCacheableSiteMapNode
{
public CustomSiteMapNode(ISiteMap siteMap, string key, bool isDynamic, ISiteMapNodePluginProvider pluginProvider, IMvcContextFactory mvcContextFactory, ISiteMapNodeChildStateFactory siteMapNodeChildStateFactory, ILocalizationService localizationService, IUrlPath urlPath) :
base(siteMap, key, isDynamic, pluginProvider, mvcContextFactory, siteMapNodeChildStateFactory, localizationService, urlPath)
{
}
protected override void PreserveRouteParameters()
{
if (this.PreservedRouteParameters.Count > 0)
{
var requestContext = this.mvcContextFactory.CreateRequestContext();
var routeDataValues = requestContext.RouteData.Values;
var queryStringValues = requestContext.HttpContext.Request.QueryString;
foreach (var item in this.PreservedRouteParameters)
{
var preservedParameterName = item.Trim().ToLower();
if (!string.IsNullOrEmpty(preservedParameterName))
{
if (routeDataValues.ContainsKey(preservedParameterName))
{
this.routeValues[preservedParameterName] =
routeDataValues[preservedParameterName];
}
else if (queryStringValues[preservedParameterName] != null)
{
this.routeValues[preservedParameterName] =
queryStringValues[preservedParameterName];
}
}
}
}
}
protected override IDictionary<string, object> MergeRouteValuesAndNamedQueryStringValues(IDictionary<string, object> routeValues, ICollection<string> queryStringKeys, HttpContextBase httpContext)
{
// Make a copy of the routeValues. We only want to limit this to the scope of the current node.
var result = new Dictionary<string, object>(routeValues);
// Add any query string values from the current context
var queryStringValues = httpContext.Request.QueryString;
// QueryString collection might contain nullable keys
foreach (var key in queryStringValues.AllKeys.Select(x => x.ToLower()).ToList())
{
// Copy the query string value as a route value if it doesn't already exist
// and the name is provided as a match. Note that route values will take
// precedence over query string parameters in cases of duplicates
// (unless the route value contains an empty value, then overwrite).
if (key != null && queryStringKeys.Contains(key) && (!result.ContainsKey(key) || string.IsNullOrEmpty(result[key].ToString())))
{
result[key] = queryStringValues[key];
}
}
return result;
}
}
下一个问题是用这个替换默认实现。我能遇到的唯一解决方案是继承工厂/容器链并最终将其绑定在 ninject 中。
public class CustomSiteMapNodeFactory : ISiteMapNodeFactory
public class CustomSiteMapNodeFactoryContainer
internal class CustomSiteMapLoaderContainer
var configSettings = new ConfigurationSettings();
MvcSiteMapProvider.SiteMaps.Loader = new CustomSiteMapLoaderContainer(configSettings).ResolveSiteMapLoader();
最后是我用来从模型中提取属性值并将它们传递给父站点地图节点的属性。
public class SiteMapRouteValueAttribute : ActionFilterAttribute
{
private readonly string _key;
private readonly string[] _propertyParts;
public SiteMapRouteValueAttribute(string key, string modelProperty = "")
{
_key = key.ToLower();
_propertyParts = modelProperty.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult == null)
{
base.OnResultExecuting(filterContext);
return;
}
var value = viewResult.Model;
foreach (var property in _propertyParts)
{
var propertyInfo = value.GetType().GetProperty(property);
value = propertyInfo.GetValue(value);
}
var currentNode = SiteMaps.Current.CurrentNode;
while (currentNode != null)
{
currentNode.RouteValues[_key] = value;
currentNode = currentNode.ParentNode;
}
}
}
所以现在它可以解释为什么任何情况下的查询字符串值名称,但是 siteMap 生成的 url 将包含小写的查询字符串值名称。