如何为Razor建立一个“循环”助手?

时间:2011-12-04 03:14:06

标签: c# asp.net-mvc-3 razor cycle helpers

基本上,我想将this helper添加到Razor。我尝试了什么:

public static class Html
{
    static Dictionary<string[], int> _cycles;

    static Html()
    {
        _cycles = new Dictionary<string[], int>();
    }

    public static string Cycle(this HtmlHelper helper, string[] options)
    {
        if (!_cycles.ContainsKey(options)) _cycles.Add(options, 0);
        int index = _cycles[options];
        _cycles[options] = (options.Length + 1) % options.Length;
        return options[index];
    }

用法:

<tr class="@Html.Cycle(new[]{"even","odd"})">

但它只是对每一行说“均匀”......不知道为什么。我不确定这个类何时被实例化.....是每个请求一次,每个服务器运行一次......还是什么?无论如何......我如何解决这个问题,以便提供预期的交替?


尝试#2

public static class Html
{
    public static string Cycle(this HtmlHelper helper, params string[] options)
    {
        if(!helper.ViewContext.HttpContext.Items.Contains("cycles"))
            helper.ViewContext.HttpContext.Items["cycles"] = new Dictionary<string[],int>(new ArrayComparer<string>());
        var dict = (Dictionary<string[], int>)helper.ViewContext.HttpContext.Items["cycles"];
        if (!dict.ContainsKey(options)) dict.Add(options, 0);
        int index = dict[options];
        dict[options] = (index + 1) % options.Length;
        return options[index];
    }
}

class ArrayComparer<T> : IEqualityComparer<T[]>
{
    public bool Equals(T[] x, T[] y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        if (x.Length != y.Length) return false;
        for (int i = 0; i < x.Length; ++i)
            if (!x[i].Equals(y[i])) return false;
        return true;
    }

    public int GetHashCode(T[] obj)
    {
        return obj.Length > 0 ? obj[0].GetHashCode() : 0;
    }
}

这有什么问题吗?

4 个答案:

答案 0 :(得分:2)

这是失败的原因,是因为你每次都使用一个全新的数组作为字典的关键。

我建议只包括一个额外的参数作为dictonary key。

为了上帝的缘故,请使用私人存储区。 (而不是static成员,当多个线程点击页面时会爆炸。)

答案 1 :(得分:1)

Cycle方法的每次调用都会传递一个新的options数组,并在字典中添加一个新密钥。

答案 2 :(得分:1)

要解决此问题和线程问题,您可以将字符串本身用作HttpContext中的键:

string key = "Cycle-"+String.Join("|", options);

if (!html.ViewContext.HttpContext.Items.Contains(key))
     html.ViewContext.HttpContext.Items.Add(key, 0);
int index = html.ViewContext.HttpContext.Items[key];
html.ViewContext.HttpContext.Items[key] = (index + 1) % options.Length;
return options[index];

请注意,这将分享子操作和部分之间的循环。

答案 3 :(得分:0)

想出我也可以用@function做到这一点......然后我就不需要将命名空间添加到Web.config

@using MvcApplication4.Helpers @* for ArrayComparer *@

@functions {
    public static string Cycle(params string[] options)
    {
        if (!HttpContext.Current.Items.Contains("Html.Cycle"))
            HttpContext.Current.Items["Html.Cycle"] = new Dictionary<string[], int>(new ArrayComparer<string>());
        var dict = (Dictionary<string[], int>)HttpContext.Current.Items["Html.Cycle"];
        if (!dict.ContainsKey(options)) dict.Add(options, 0);
        int index = dict[options];
        dict[options] = (index + 1) % options.Length;
        return options[index];
    }
}

也可以用@helper来做,但我认为它不会那么干净。