将属性与 .net 中的代码生成属性相关联

Posted

技术标签:

【中文标题】将属性与 .net 中的代码生成属性相关联【英文标题】:Associate attribute with code generated property in .net 【发布时间】:2010-10-02 04:19:50 【问题描述】:

我希望在 .NET 中为公共属性设置属性,但是我无权访问显式属性本身,因为这是在另一个文件中生成的代码。

我有这个字段:

public virtual string Name  get; set; 

我想设置这个:

[ValidateNonEmpty("Name is required", ExecutionOrder = 1)]
public virtual string Name  get; set; 

我的课程被标记为部分,但你不能有部分属性。我以为我对 MetadataType 类有所了解,这是 Dynamic Data 和 DataAnnotations 的一个新功能,但是我觉得它只能与 Dynamic Data 一起使用,这是真的吗?

引用: http://blogs.oosterkamp.nl/blogs/jowen/archive/2008/10/16/metadatatype-attribute.aspx http://blogs.msdn.com/davidebb/archive/2008/06/16/dynamic-data-and-the-associated-metadata-class.aspx

有什么方法可以设置这个属性(甚至通过 web.config!)而不接触代码生成的类?

提前致谢, 格雷厄姆

【问题讨论】:

【参考方案1】:

这是一个已知的麻烦;您根本无法将元数据添加到生成的成员中。

这里有 6 个选项(按努力增加的顺序):

如果您拥有该属性,则可以针对类声明它,例如:[ValidateNonEmpty("Name", "Name is required", ExecutionOrder = 1)] - 然后将多个属性添加到部分类定义中 使用虚拟/接口/等方法来查询这个,而不是通过属性来查询 子类化生成的类型;覆盖或重新声明成员,添加元数据(真的很乱) 使用自定义TypeDescriptionProvider 来提供动态元数据(大量工作)——假设消费者尊重TypeDescriptor;大多数与绑定相关的消费者都这样做,但例如,Expression(许多 LINQ 提供者使用)不这样做 更改代码生成器/自己编写 尝试扩展 PostSharp 之类的东西来完成这项工作(我还没有找到这样做的方法,但我很想听听您是否找到方法!)

我通常使用第一个选项会成功,除非它是系统定义的属性([DisplayName] 等)。如果[ValidateNonEmpty] 是由动态数据定义的,那么您可能无法做到这一点。

【讨论】:

谢谢 Marc,我想可能是这样。我已经设法遍历了我的“MetadataType”声明类的属性,此时我希望查询属性,我只是将“元”属性的名称与真实属性进行比较。 这与调查真实属性不同,我理解,但对于我需要的东西,它看起来可以在这种情况下达到目的。太棒了。 希望这是有道理的。我现在可以查看是否声明了验证属性并相应地工作。我现在只希望使用MetadataType 属性类而不是自己创建一个简单地告诉它查看属性的类没有开销。【参考方案2】:

由于生成的类是一个部分类,以下应该可以工作:

    创建一个在其中声明此属性的接口,并使用 ValidateNonEmpty 属性对其进行装饰。 创建您自己的与 AutoGenerated 类同名的分部类,并使其实现您刚刚创建的接口。 现在应该使用该属性装饰属性

例如:

// Decorate the properties with attributes as required
public interface IMyInterface

    [ValidateNonEmpty("Name is required")]
    string Name  get; set; 


// This is the partial class I created, that implements the interface
public partial class MyGeneratedClass : IMyInterface
    


// This is the auto-generated class
public partial class MyGeneratedClass

    public virtual string Name  get; set; 

我从geekswithblogs得到这个想法。

【讨论】:

我认为您需要将基类中的每个属性也放入接口中,如果您有许多生成的类和许多属性,这是一个乏味的练习。 链接(实际上)已损坏。它重定向到 Geekswithblogs.net, the End of an Era.【参考方案3】:

这是一个很好的解决方案,但它对我的问题不起作用。我将 EF 6 与现有数据库中的代码优先生成的类一起使用。表中的一列是具有自动生成值的 IDENTITY。但是,生成的部分类没有提供数据库生成密钥所需的 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 属性。结果是错误“当 IDENTITY_INSERT 设置为 OFF 时,无法在表 'mytable' 中插入标识列的显式值。”。我尝试了您的解决方案,但没有奏效。但是,如果我将属性添加到原始生成的类中,它确实有效。所以我还在努力寻找不需要修改自动生成文件的解决方案。

这是我尝试使用您的解决方案的代码:

public interface IMyTable

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    int ID  get; set; 


public partial class MyTable : IMyTable


原始生成代码:

[Table("MyTable")]
public partial class MyTable

    [Key]
    [Column(Order = 1)]
    public int ID  get; set; 

【讨论】:

【参考方案4】:

另一种选择是将属性包装在同一类中的非生成属性中。不理想,因为您最终可能会拥有双重属性,但如果您可以让生成器生成受保护的属性,那将是一个非常好的方法。

只需要处理这个问题:Entity Framework 生成类,我想用更简单的名称将它们序列化为 JSON。

// GENERATED BY EF
public partial class ti_Users

    public ti_Users()
    
        this.ti_CardData = new HashSet<ti_CardData>();
        this.ti_Orders = new HashSet<ti_Orders>();
    

    protected int userId  get; set; 
    protected string userName  get; set; 
    protected string userEmail  get; set; 
    protected string userPassHash  get; set; 
    protected Nullable<System.DateTime> userLastLogin  get; set; 
    protected string userLastIP  get; set;  

    public virtual ICollection<ti_CardData> ti_CardData  get; set; 
    public virtual ICollection<ti_Orders> ti_Orders  get; set; 

和附加类:

[JsonObject(memberSerialization: MemberSerialization.OptIn)]
public partial class ti_Users

    [JsonProperty]
    public int UserId
    
        get  return this.userId; 
        set  this.userId = value; 
    

    [JsonProperty]
    public string Name
    
        get  return this.userName; 
        set  this.userName = value; 
    

    [JsonProperty]
    public string Email
    
        get  return this.userEmail; 
        set  this.userEmail = value; 
    

    [JsonProperty]
    public string PassHash
    
        get  return this.userPassHash; 
        set  this.userPassHash = value; 
    
 

【讨论】:

以上是关于将属性与 .net 中的代码生成属性相关联的主要内容,如果未能解决你的问题,请参考以下文章

C#之特性(Attribute)

『 再看.NET7』泛性特性使用场景

如何使用 RuntimeTypeModel 将 ProtoInclude 与 protobuf-net 中的类型相关联?

C#中的 特性 详解(转载)

C# 特性详解

构建布局良好的Windows程序