Генерировать представление JavaScript из Enums

У меня есть несколько [Flags] перечислений в моем коде, который я хотел бы выставить на JavaScript без копирования и вставки. SignalR, похоже, делает что-то подобное для своих Hub-прокси, сопоставляя URL-адрес с Action, возвращающим заглушки JavaScript, созданные отражением. Поскольку код генерируется во время выполнения, не представляется возможным включить его в Bundles.

В качестве альтернативы я внедрил шаблон T4 для генерации js файла во время разработки:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="EnvDte" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".js" #>
Enums = {
<#
    var visualStudio = (Host as IServiceProvider).GetService(typeof(EnvDTE.DTE))
                        as EnvDTE.DTE;
    var project = visualStudio.Solution.FindProjectItem(this.Host.TemplateFile)
                                        .ContainingProject as EnvDTE.Project;

    foreach(EnvDTE.ProjectItem item in GetProjectItemsRecursively(project.ProjectItems))
    {
        if (item.FileCodeModel == null) continue;
        foreach(EnvDTE.CodeElement elem in item.FileCodeModel.CodeElements)
        {
            if (elem.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)
            {
                foreach (CodeElement innerElement in elem.Children)
                {
                    if (innerElement.Kind == vsCMElement.vsCMElementEnum)
                    {
                        CodeEnum enu = (CodeEnum)innerElement;
#>  <#= enu.Name #>: {
<#
        Dictionary<string, string> values = new Dictionary<string, string>();
        foreach (CodeElement child in enu.Members)
        {
            CodeVariable value = child as CodeVariable;

            if (value != null) {
                string init = value.InitExpression as string;
                int unused;
                if (!int.TryParse(init, out unused))
                {
                    foreach (KeyValuePair<string, string> entry in values) {
                        init = init.Replace(entry.Key, entry.Value);
                    }
                    init = "(" + init + ")"; 
                }
                values.Add(value.Name, init);
                WriteLine("\t\t" + value.Name + ": " + init + ",");
            }
        }
#>
    },
<#
                    }
                }
            }
        }
    }
#>
};
<#+
  public List<EnvDTE.ProjectItem> GetProjectItemsRecursively(EnvDTE.ProjectItems items)
  {
      var ret = new List<EnvDTE.ProjectItem>();
      if (items == null) return ret;
      foreach(EnvDTE.ProjectItem item in items)
      {
        ret.Add(item);
        ret.AddRange(GetProjectItemsRecursively(item.ProjectItems));
      }
      return ret;
  }
#>

Однако это кажется хрупким с EnvDTE. Особенно логика для обработки перечислений вроде:

[Flags]
public enum Access
{
    None = 0,
    Read = 1,
    Write = 2,

    ReadWrite = Read | Write
}

с составными значениями является грязным взломом с заменой строки. Шаблон T4 выше будет генерировать:

Enums = {
    Access: {
        None: 0,
        Read: 1,
        Write: 2,
        ReadWrite: (1 | 2),
    },
};

Есть ли более чистый способ достичь этого? В идеале какое-то отражение времени проектирования для генерации js файла, поэтому оно доступно для группировки.

Ответ 1

Я думаю, вы можете использовать Bundle Transforms для выполнения этого...

http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

Вы можете определить пакет и прикрепить класс IBundleTransform, который будет интерполировать содержимое исходных файлов. Вы должны иметь возможность использовать отражение для записи JavaScript в выходной поток.

Если вы хотите сделать это взломанным способом, это будет довольно легко сделать. Вы могли бы просто прокормить его пустым файлом и жестко закодировать свой класс, чтобы использовать отражение, чтобы написать нужный JavaScript.

Теперь, если вы хотите сконструировать его таким образом, что вам не нужно модифицировать свой класс IBundleTransform, чтобы добавить дополнительные перечисления в выходной файл javascript, вам нужно добавить некоторую дополнительную работу в реальную структуру. Например, предположим, что у вас есть все ваши перечисления в файле с именем Enums.cs, вы можете добавить этот файл в список Include вашего пакета, и вы могли бы динамически разобрать его для деклараций перечисления, а затем использовать отражение, чтобы найти их по имени в порядке для вывода каждого из них в результирующий файл JavaScript.

Ответ 2

Мне нравится использовать пользовательские атрибуты и действие контроллера в моем приложении. Во время разработки я добавляю значение перечисления и нажимаю "Выполнить". Я просматриваю действие своего контроллера (ссылку, доступную только во время отладки). Контроллер сканирует все мои перечисления, которые реализуют пользовательский атрибут [GenerateJavascriptEnum] и voila, я вижу всплывающее окно браузера со всем красивым javascript. Я копирую/вставляю и обновляю браузер, чтобы получить изменения на клиенте. Это было очень удобно с минимальной суматохой.