在ASP.NET MVC中创建带有DateTime的查询字符串的ActionLink

5

我有一个搜索表单,其中包括一个日期时间搜索条件以及其他一些条件:

<form method="get" action="/app/search">
    <input type="text" value="13/01/2010" name="BeginDate"/>
    <input type="text" value="blah" name="SomeOtherCriterion"/>
<form>

我有一个搜索控制器,它有一个默认操作(我们称其为Index)和一个SearchCriteria参数。

public class SearchController
{
    public ActionResult Index(SearchCriteria searchCriteria) {//blah }
}    

public class SearchCriteria
{
    public DateTime BeginDate {get; set;}
    public string SomeOtherCriterion {get; set;}
}

现在,如果我想创建一个ActionLink,并传递一个SearchCriteria值,可以这样做:
Html.ActionLink("Search", "Index", searchCriteria)

我在查询字符串参数中获取了以美国格式表示的BeginDate。通过在Google上搜索并使用反射查看System.Web.Routing,似乎是因为它使用了InvariantCulture,所以我对此无能为力。

似乎没有人在之前提出过这个问题,所以我想我可能做了些非常愚蠢的事情... 请帮忙!

编辑:将SearchCriteria传递给ActionLink而不是匿名对象,以显示为什么我不能自定义ToString()。

7个回答

4
考虑到该框架似乎是使用InvariantCulture硬编码来处理这个数据片段的,我认为你不能做太多事情使其透明地工作。
有一种丑陋的选择——下载MVC源代码并从Route到ParsedRoute中删除所有有问题类的代码,创建自己的RouteBase实现以实现所需功能。
如果我绝对必须在SearchCriteria类上保留DateTime声明,那么这就是我会选择的路线(抱歉,双关语)。
但是,一个更简单的解决方案是将您的SearchCriteria类更改为使用稍微不同的DateTime字段声明类型,例如:
public class MyDateTime
{
  public DateTime Value { get; set; }
  //for passing MyDateTime in place of a DateTime without casting
  public static implicit operator DateTime(MyDateTime instance) { return instance.Value; }
  //so you can assign a MyDateTime from a DateTime without a cast
  //- e.g. MyDateTime dt = DateTime.Now
  public static implicit operator MyDateTime(DateTime instance) { return new MyDateTime() { Value = instance }; }
  //override ToString so that CultureInfo.CurrentCulture is used correctly.
  public override string ToString()
  {
    return Value.ToString(CultureInfo.CurrentUICulture);
  }
}

理论上,你应该能够轻松地推出这个变化。

如果你有很多使用DateTime实例成员(例如.Days等)的代码,则可能需要大量工作来处理SearchCriteria:你必须在MyDateTime上重新生成这些成员,包装内部DateTime值,或更改所有代码以使用.Value.Member。


对我们来说并不是很实用,但绝对是最有创意的解决方案,所以我会接受它。谢谢Zoltan。 - Gaz

2
为避免与区域设置和“文化”相关的问题, 我将日期和时间视为单独的未绑定字段, 然后在我的控制器中将它们组合成DateTime。
例如: 年[ ] 月[ ] 日[ ]
我总是按照年、月、日的顺序分别提供文本框, 以便美国格式(月/日/年)和其他大多数国家/地区的格式(日/月/年)之间不会混淆。

1
你能在ActionLink中提供一个格式化的日期吗?试试这个:
Html.ActionLink("Search", 
                "Index", 
                new {BeginDate = 
                      DateTime.Now.ToString("d", new CultureInfo("pt-BR");})

当然,这会将BeginDate更改为字符串而不是DateTime...但也许这对您有用?


我希望能够直接传递一个SearchCriteria而不是使用匿名对象。我知道我在问题中漏掉了一些东西! - Gaz
啊,这有点难。我猜你可以匿名化SearchCriteria只是为了格式化日期字符串,但那会很麻烦... - womp
有人给你踩了一下,呜呼。虽然你拥有23000点赞,估计你也不在乎,但还是觉得有点刻薄。为了弥补一下,我给你点了个赞。 - Gaz

1
poking around in System.Web.Routing using Reflector it

seems to be because it uses the InvariantCulture

你确定这个吗?我检查了模型绑定和 URL 构建的部分,它们都使用了当前区域设置。你可以在渲染链接之前设置当前区域设置来检查一下会发生什么。


我关注的部分是System.Web.Routing.ParsedRoute.Bind。在底部,它将任何“未使用的值”放入查询字符串中。(尽管此方法中的每个ToString都使用InvariantCulture。)我尝试设置CurrentCulture。但没有帮助。 - Gaz

1
我们使用 ISO 格式(在格式字符串中为 "s" -- YYYY-MM-DDTHH:MM:SS)来处理此问题。它可以正常工作,JavaScript 也可以处理它。

1
但是,你如何获取ActionLink或任何其他使用RouteValueDictionary将DateTime转换为此格式的MVC方法? - Gaz
只需将其作为字符串输入:new RouteValueDictionary(new { SomeDate = anotherDate.ToString("s") }) - Craig Stuntz

1
也许你可以使用模型绑定器来格式化和解析日期?刚刚重新阅读了文章,发现它并没有格式化日期...可能行不通。但我还是会保留答案,以防它提供任何无意的灵感 :)

谢谢Chris。据我所知,它只适用于处理传入的请求,无法帮助使用ActionLink创建URL。 - Gaz

1
获取由Scott Hanselman、Scott Guthrie、Phil Haack和Rob Conery编写的ASP.NET MVC 1.0书籍。他们在书中实际上使用了这个精确的情景,使用了特定的路由。我现在正在第216页看它。
他们通过拆分日、月和年来实现。然后,您需要使用返回的这些值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接