在VS2013 T4脚手架模板中获取自定义属性

时间:2014-06-17 13:06:58

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

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

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

<#+
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 Controller / View Scaffolder中获取PropertyInfo对象?
  • 如果没有,访问自定义注释的新/正确方法是什么? ModelMetadata对此
  • 似乎毫无用处

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

2 个答案:

答案 0 :(得分:3)

我已在此处编写了有关如何完成此操作的教程: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;
         }
     }
}

答案 1 :(得分:0)

我也一直在努力解决这个问题。我找到了一种解决方法,通过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类,我不知道更好的解决方案是什么。

希望这是有道理的。