【问题标题】:Getting Custom Attributes in VS2013 T4 Scaffolding Templates在 VS2013 T4 脚手架模板中获取自定义属性
【发布时间】:2014-06-17 13:06:58
【问题描述】:

我正在移植我以前的 MVC4.5 T4 脚手架模板以在 VS2013 中使用。一切都很顺利,幸运的是它背后的逻辑并没有太大变化,但是很多命名空间、对象和属性都像我预期的那样被重命名了。

然而,棘手的一点是PropertyInfo。似乎不再可以使用PropertyInfo,因为新的ModelMetadata 对象只包含PropertyMetadata。由于PropertyMetadata 没有GetCustomAttributes() 方法或类似方法,我坚持升级以下sn-p:

<#+
string SearchableBy(PropertyInfo property) {
    foreach (object attribute in property.GetCustomAttributes(true))
    {
        var searchable = attribute as SearchableAttribute;
        if (searchable != null)
        {
            return searchable.By == "" ? GetValueExpressionSuffix(property) :
                                         searchable.By;
        }
    }
    return null;
}
#>
  • 是否可以通过某种方式在 T4 控制器/视图脚手架中获取 PropertyInfo 对象?
  • 如果不是,访问自定义注释的新/正确方法是什么? ModelMetadata 似乎对此毫无用处

PS:
这个问题可以认为是my previous one的子问题
如果您对如何在 VS2012 中访问自定义注释感兴趣,请参阅this one

【问题讨论】:

    标签: visual-studio-2013 data-annotations t4 asp.net-mvc-scaffolding


    【解决方案1】:

    我在此处编写了有关如何完成此操作的教程:https://johniekarr.wordpress.com/2015/05/16/mvc-5-t4-templates-and-view-model-property-attributes/

    使用此自定义属性

    namespace CustomViewTemplate
    {
         [AttributeUsage(AttributeTargets.Property)]
         public class RichTextAttribute : Attribute
         {
             public RichTextAttribute() { }
         }
    }
    

    在您的实体中,使用自定义属性装饰属性

    namespace CustomViewTemplate.Models
    {     
         [Table("Person")]
         public class Person
         {
             [Key]
             public int PersonId { get; set;}
    
             [MaxLength(5)]
             public string Salutation { get; set; }
    
             [MaxLength(50)]
             public string FirstName { get; set; }
    
             [MaxLength(50)]
             public string LastName { get; set; }
    
             [MaxLength(50)]
             public string Title { get; set; }
    
             [DataType(DataType.EmailAddress)]
             [MaxLength(254)]
             public string EmailAddress { get; set; }
    
             [DataType(DataType.MultilineText)]
             [RichText] \\ Custom Attribute
             public string Biography { get; set; }     
         }
    }
    

    然后创建一个我们将在模板中引用的 T4Helper

    using System; 
    
    namespace CustomViewTemplate
    {
         public static class T4Helpers
         {
             public static bool IsRichText(string viewDataTypeName, string propertyName)
             {
                 bool isRichText = false;
                 Attribute richText = null;
                 Type typeModel = Type.GetType(viewDataTypeName);
    
                 if (typeModel != null)
                 {
                     richText = (RichTextAttribute)Attribute.GetCustomAttribute(typeModel.GetProperty(propertyName), typeof(RichTextAttribute));
                     return richText != null;
                 }
    
                 return isRichText;
             }
         }
    }
    

    【讨论】:

      【解决方案2】:

      我也一直在努力解决这个特殊问题。我找到了一种解决方法,可以通过 T4 模板可用的 EnvDTE 服务访问模型对象属性的属性集合。不过有点曲折,也有一些限制。

      创建 EnvDTE 服务实例后,您需要引用包含您的 EF DBContext 的项目(假设您的 MVC/WebAPI 位于同一解决方案中的单独项目中)。如果 DBContext 在不同的解决方案中并且无法通过 EnvDTE 服务获得,那么我不确定您是否可以在不引用 EF 程序集和使用反射的情况下实现目标;但这会降低 T4 模板的可移植性。

      下面是代码的草图。在我的实际实现中,我有一个例程来定位包含 DBContext 类的项目。下面我通过名称来引用 EF 项目。

      var env = (DTE)((IServiceProvider)this.Host).GetService(typeof(EnvDTE.DTE));
      var project = dte.Solution.Projects.OfType<Project>().Where(p => p.Name == "your ef project name").FirstOrDefault();
      
      CodeType codeType = project.CodeModel.CodeTypeFromFullName("your ef namespace.class");
      CodeClass cc = (CodeClass)codeType;
      
      List<CodeProperty> cps = cc.Members.OfType<CodeProperty>().ToList();
      
      WriteLine(codeType.FullName);
      WriteLine("======================");
      
      foreach (CodeProperty cp in cps)
      {
          WriteLine(cp.Name);
          foreach (CodeAttribute ca in cp.Attributes.OfType<CodeAttribute>())
          {
              WriteLine("-" + ca.Name);
          }
      }
      

      如果您发现属性的 attibutes 集合为空,那是因为 EF 模型不在您引用的 EnvDTE.Project 中。所以你肯定需要通过 EF 模型所在的项目来访问 CodeClass 对象。

      同样,这是一个粗略的草图...我相信您需要稍微调整一下才能获得所需的结果。我确信必须有更好的方法来做到这一点,但除了创建一个自定义脚手架,它使用属性值扩展 ModelMetadata 类之外,我不确定更好的解决方案是什么。

      希望这是有道理的。

      【讨论】:

      • 嗯...我没有时间尝试这个,但由于很长一段时间内没有其他人可以贡献,我会接受这个答案。一旦我有机会再次深入研究这一点,我将分享我的发现。干杯!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多