在MVC Razor中分享C#和Javascript之间的枚举

jaf*_*ffa 13 javascript razor asp.net-mvc-3

我已经看到了将常量添加到javascript文件中的问题的有用答案,因此它可以与razor视图一起使用:在MVC Razor中共享C#和Javascript之间的常量

我希望能够做同样的事情,除了定义枚举,但我不知道如何将C#Enum转换为javascript中的常量.

关闭GetType(),似乎没有办法实际获得常量值.

val*_*ero 11

我从几个人的答案中得到了一个混合物并编写了这个HtmlHelper扩展方法:

    public static HtmlString GetEnums<T>(this HtmlHelper helper) where T : struct
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        sb.AppendLine("<script type=\"text/javascript\">");
        sb.AppendLine("if(!window.Enum) Enum = {};");
        var enumeration = Activator.CreateInstance(typeof(T));
        var enums = typeof(T).GetFields().ToDictionary(x => x.Name, x => x.GetValue(enumeration));
        sb.AppendLine("Enum." + typeof(T).Name + " = " + System.Web.Helpers.Json.Encode(enums) + " ;");
        sb.AppendLine("</script>");
        return new HtmlString(sb.ToString());
    }
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用Razor语法调用该方法,如下所示: @(Html.GetEnums<Common.Enums.DecisionStatusEnum>())

然后会吐出这样的javascript:

<script type="text/javascript">
    if(!window.Enum) Enum = {};
    Enum.WorkflowStatus = {"value__":0,"DataEntry":1,"SystemDecisionMade":2,"FinalDecisionMade":3,"ContractCreated":4,"Complete":5} ;
</script>
Run Code Online (Sandbox Code Playgroud)


然后你可以在这样的javascript中使用它:

if(value == Enum.DecisionStatusEnum.Complete)
Run Code Online (Sandbox Code Playgroud)

由于top(if(!window.Enum))的属性检查,这允许您为多个枚举调用它,它不会覆盖全局 Enum变量,只是附加到它.

  • 你可以使用:var enums = Enum.GetValues(typeof(T)).Cast <int>().ToDictionary(x => Enum.GetName(typeof(T),x),y => y); 所以你没有得到那个"价值___" (7认同)

Jos*_*ell 7

转换为Dictionaryfor enum只是略有不同.

public ActionResult DayOfWeekEnum()
{
    var enumType = typeof(DayOfWeek);
    var enumDictionary = enumType
        .GetFields()
        .Where(x => x.IsLiteral)
        .ToDictionary(x => x.Name, x => (int)Enum.Parse(enumType, x.Name));
    var json = new JavaScriptSerializer().Serialize(enumDictionary);
    return JavaScript("var DayOfWeek = " + json + ";");
}
Run Code Online (Sandbox Code Playgroud)

您可以更进一步并传入命名空间enum并使用反射来查找类型.这将允许更通用的操作方法.

ActionResult Enum(string enumType)
Run Code Online (Sandbox Code Playgroud)


J. *_*mes 7

我之前使用的另一种方法是使用t4模板来完成这种工作.类似于t4mvc的工作方式,如果你知道如何使用它,它可以是一个非常强大的工具.

这个想法是在t4模板中你爬行你的项目并寻找一个枚举,当它找到一个时,你会想要模板来转换它并根据C#代码吐出一些javascript.我第一次使用T4Templates时,它突然爆发了,并没有很多很好的资源,但它们可以是特殊的(参见t4mvc)

在您链接到的另一个问题中使用模板而不是控制器操作的优点是,t4模板引擎生成的文件是输出是常规js文件,可以像任何其他JavaScript文件一样提供/缩小,而不是要求MVC堆栈的开销以满足请求.

如果你有兴趣,我可能会挖掘一个例子.我可能没有在存储库中存放过.

编辑

所以我挖了一个例子,我从它的原始形式编辑了一下,并没有测试它,但你应该得到这个想法.它有点长,所以我把它作为一个github要点.但我会在这里强调一些重要的块.

首先,T4模板是Visual Studio内置的模板引擎,控制块是用C#编写的(如果需要,可以用VB编写).我不是想象中的专家,也不是自称是一个人,但我会分享我能做到的.因此,这些文件在Visual Studio项目中显示类似于其他"代码隐藏"类型的项目,您可以在其中展开.tt项目并查看其后面生成的模板文件.

所以让我们深入研究:

<#@ template language="C#v3.5" debug="true" hostspecific="true" #>

第一行设置控制块的语言.如你所见,我将使用C#.

<#@ output extension=".js" #>

接下来,我正在设置生成的文件的扩展名.在这种情况下,我说我想生成一个.js文件.因此,当我将此模板放入解决方案enum.tt时,当我运行模板时,它将创建一个名为的文件enum.js.您可以控制生成的文件(或多个文件).例如,t4mvc可以选择生成一堆不同的文件(每个控制器一个)或生成一个t4mvc.cs文件.

接下来,您将找到我需要使用的一堆程序集.一些更有趣的是以下内容:

<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #> 
Run Code Online (Sandbox Code Playgroud)

同样,我不是专家,但您可以在msdn网站上找到这些文档.这些提供了一些核心功能,可以访问/操作visual studio解决方案.

然后有一些相当无趣的进口.你会注意到控制块是由<# .. #>(或者说,我真的不记得下一个字符的重要性,它已经有一段时间了).任何没有包含在控制块中的东西都将直接写入输出流.

这将我们带到将要编写的实际文件的开头:

window.Enum = function() {
    this.__descriptions = [];
    this.__ids = []
    this.__last_value = 0;
}

window.Enum.prototype.add = function(name, val) {
    if(val == undefined) val = ++this.__last_value;
    this[name] = val;
    this[val] = name;
    this.__ids[val] = name;
    this.__descriptions[val] = name.replace(/ShowWithEllipses$/,"...").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^\s+/,"");
    return this;
}

window.Enum.prototype.describe = function(val) { return this.__descriptions[val] };
Run Code Online (Sandbox Code Playgroud)

在这里,我只是做一个简单的JavaScript Enum实现.没有声称它是最好的.但是它就是这样啊.:)

<#
Prepare(this);
foreach(ProjectItem pi in FindProjectItemsIn(CurrentProject.ProjectItems.Item("Models"))) {
    DumpEnumerationsFrom(pi);
}
#>
Run Code Online (Sandbox Code Playgroud)

然后我们得到了模板的内容.基本上它看起来在一个名为的文件夹中Models.并挖掘并试图找到它能找到的任何枚举.如果是这样,它会调用以下方法:

void DumpEnumerationsFrom(ProjectItem file) {
    var enumerations = new List<CodeEnum>();
    FindEnum(file.FileCodeModel.CodeElements, enumerations);

    if(enumerations.Count > 0) TT.WriteLine("// {0}",file.Name);

    foreach(CodeEnum enumeration in enumerations) {
        TT.Write("window.Enum.{0}=(new Enum())", enumeration.Name);
        foreach(CodeElement ce in enumeration.Children) {
            var cv = ce as CodeVariable;
            if(cv == null) continue;
            TT.Write("\r\n\t.add(\"{0}\", {1})", cv.Name, cv.InitExpression ?? "undefined");
        }
        TT.WriteLine(";\r\n");
    }
}
Run Code Online (Sandbox Code Playgroud)

它会产生如下所示的东西:

window.Enum.TheNameOfTheEnum = (new Enum()).add("Value1",1).add("Value2",2);
Run Code Online (Sandbox Code Playgroud)

因此生成的JS文件直接基于c#项目中的枚举.

但是有一些限制.一个枚举必须位于项目中的文件中(不在引用的库中),至少使用此实现可能有更聪明的方法来执行它.每次更改枚举时,都必须重新运行模板(右键单击它并选择"运行自定义工具").

但是有一些优点,就像我之前提到的那样,生成的文件只是一个简单的js文件,因此可以组合并运行缩小.因为它只是一个文件,它可以托管在CDN上,就像我之前提到的那样,不需要命中MVC堆栈来提供请求.

无论如何,我并不是说它是出于各种目的的最好的想法,但在我看来它是一种未被充分利用的方法.希望这可能有助于提供一些启示,并为您提供调查方向.