要继续我以前的一个问题(Unexpected route chosen while generating an outgoing url),请考虑以下路线(无默认值,无约束):
"{controller}/{month}-{year}/{action}/{user}"
假设在通过匹配此路由的传入URL呈现某些页面时(因此请求上下文包含每个路径段的值),我需要生成仅更改month
和year
的网址,保留所有其他段的确切值。
根据规则,我在链接的问题中提到过(即路由系统将仅重用URL模式中较早出现的段变量的值,而不是任何提供的参数。),如果我只通过匿名对象指定新的month
和year
,我将丢失user
段的值(即路由甚至不匹配)。
我知道有两种方法可以解决这个问题:
根本不指定匿名对象;但在context.RouteData.Values
中设置必要的段值;在生成所需的url之后,返回原始值,以便使用原始请求段值执行其余页面呈现;这看起来像这样:
public static MvcHtmlString GetDateUrl(this RequestContext context,
DateTime date)
{
UrlHelper url = new UrlHelper(context);
object month = null, year = null;
if (context.RouteData.Values.ContainsKey("month"))
{
month = context.RouteData.Values["month"];
year = context.RouteData.Values["year"];
}
try
{
context.RouteData.Values["month"] = date.Month;
context.RouteData.Values["year"] = date.Year;
return MvcHtmlString.Create(
url.Action((string)context.RouteData.Values["action"]));
}
finally
{
if (month == null)
{
if (context.RouteData.Values.ContainsKey("month"))
{
context.RouteData.Values.Remove("month");
context.RouteData.Values.Remove("year");
}
}
else
{
context.RouteData.Values["month"] = month;
context.RouteData.Values["year"] = year;
}
}
}
根据当前请求上下文,使用RouteValueDictionary
而不是匿名对象:
public static MvcHtmlString GetDateUrl(this RequestContext context,
DateTime date)
{
UrlHelper url = new UrlHelper(context);
var values = new RouteValueDictionary(context.RouteData.Values);
values["month"] = date.Month;
values["year"] = date.Year;
string action = (string)context.RouteData.Values["action"];
return MvcHtmlString.Create(url.Action(action, values));
}
第二个选项似乎更简短,而第一个选项不会创建额外的字典,操纵请求上下文。
哪种方式更好?或者也许我正在做/理解错误的方法?
Sanderson S.,Freeman A. - Pro ASP.NET MVC 3 Framework(第3版)表示不依赖于段值重用,但确实指定了确切位置所需的所有值时间。
我展示了我发现执行此任务的方式,但是如果替换的段在路径的早期发生,那么替换段的值(保留其他值)的正确(或更简单)方法是什么?
修改
基于JaniHyytiäinen的回答。有问题的路线只是一个例子。目标mvc应用程序包含不同的路由,有些可能比这个更复杂。所以我正在寻找的方法应该能够与它们中的任何一个一起工作。我想这个扩展方法只知道,它必须更改month
和year
,同时保留所有其他段。 其他段的数量是任意的。所以硬编码所有这些根本不是一个可能的解决方案。除了请求上下文本身之外,我还有当前段值的来源。
答案 0 :(得分:2)
public static MvcHtmlString GetDateUrl(this RequestContext context, DateTime date)
{
var values = context.RouteData.values;
var data = new{
controller = values["controller"] ?? "Home";
action = values["action"] ?? "Index";
month = date.Month;
year = date.Year;
user = context.HttpContext.User.Identity.Name;
};
string url = UrlHelper.RouteUrl(data);
return MvcHtmlString.Create(url);
}
为什么要让它变得更复杂?你可能会争辩说你想对HttpContext做一些空检查,但是当你有一个没有HttpContext的RequestContext时,这里的空检查是你的问题。无论是那个,还是你有一个程序员做一些超级时髦的东西,你想让应用程序在那里吹嘘它的所有荣耀,并让他们知道他们正在做一些不起作用的东西,他们应该在它之前就停在那里生产
你也可能会在这里做任何其他事情的额外检查,但实际上,这种方法的责任在这里结束。它应该接受一个日期并返回一个路由URL作为MvcHtmlString。周期。
从性能角度来看,您可以在生成网址时在此明确定义特定的路由名称,但为了使其有意义,您需要拥有相当大的路由集合。考虑到您将在此方法和路由配置之间创建隐藏的依赖关系。当有人更改路线名称时,应用程序会爆炸,很难找到原因。
您可能还想使用RouteValueDictionary,但没有理由。如果我没记错的话,UrlHelper会在内部使用对象。即使我记得它错了,它也能很好地为你转换它。
参考文献: “确定在确切的地点和时间指定所需的所有值”,Sanderson S.,Freeman A. - Pro ASP.NET MVC 3 Framework
http://msdn.microsoft.com/en-us/library/dd505226(v=vs.108).aspx