可以制作一个 C# 属性,该属性是 Entity Framework 代码优先迁移获取的其他属性的组合

Can one make a C# attribute that is a combination of other attributes that get picked up by Entity Framework Code first migrations

我想减少重复代码,其中我为多个对象和关联数据库 table 中使用的对象 属性 指定了相同的属性。

我正在使用 属性 属性来定义 属性 应该如何保存在数据库中以及它应该如何在 UI 元素中命名。在我的例子中,这个 属性 出现在多个 tables/objects 中,我希望它在任何地方都具有相同的属性。我还希望 Entity Framework 的代码优先迁移能够获取这些属性。看起来代码优先迁移循环遍历属性并查找特定的 classes,例如 MaxLengthAttribute 或继承自特定 classes 的 classes。可惜 Entity Framework 没有寻找接口。

我不想将此字符串移动到不同的 table,因为将使用这些 table 的客户希望它们被 "CustomerNo" 直接查询。

例如:

[Table("foo")]
public class foo {
   …

   [Column(TypeName="varchar")]
   [MaxLength(15)]
   [Display(Name="Customer Identifier")]
   public string CustomerNo {get; set;}
   …
}

[Table("bar")]
public class bar {
   …

   [Column(TypeName="varchar")]
   [MaxLength(15)]
   [Display(Name="Customer Identifier")]
   public string CustomerNo {get; set;}
   …
}

我想做的是制作一个自定义属性,将上述属性组合成 [CustomerNoAttribute](我知道我可以省略后缀 "Attribute",它可以减少 class客户编号).

没有多重继承,所以我不能只继承 ColumnAttribute、MaxLengthAttribute 和 DisplayAttribute。

有什么方法可以使用合成来完成这项工作吗?例如

下面的代码无效。新的内部属性没有附加到我放置 [CustomerNoAttribute] 的属性。

public CustomerNoAttribute: Attribute {

     public CustomerNoAttribute() {
          new MaxLengthAttribute(15);
          new DisplayAttribute().Name = "Customer Identifier";
          new ColumnAttribute().TypeName = "nvarchar";
     }
}

还有其他方法可以减少这种重复吗?

使用 运行 时间添加属性的技术不会有帮助,因为它看起来像 entity framework 的代码首次迁移只查看编译时属性。

这里的解决方案比较简单,也是我最喜欢的功能之一 Entity Framework:

Code First Conventions

See Custom Code First Conventions for a full run through, the concept is that you can define your own arbitrary rules or conventions that the EF runtime should obey, this might be based on attributes but it doesn't have to be. You could create a convention based on the suffix or prefix on a field name if you really wanted.

Custom Conventions is a similar mechanism to Custom Type Descriptors as explained in this solution , except specifically for Entity Framework Code First models

你是在正确的轨道上,制作自定义属性简化了自定义代码约定的实现,然而显示属性是有问题的......通常我会建议从提供最多配置的属性继承,在这种情况下DisplayAttribute,但我们不能继承该类型,因为它是密封的。 不幸的是,我将 DisplayAttribute 排除在该解决方案之外,因为在消费者端可以采用不同的约定概念。这反而展示了如何使用自定义属性替换多个基于 DataAnnotationAttributes.

public CustomerNoAttribute : Attribute {
}

public class CustomerNoConvention : Convention
{
    public CustomerNoConvention()
    {
        this.Properties()
            .Where(p => p.GetCustomAttributes(false).OfType<CustomerNoAttribute>().Any()
            .Configure(c => c.HasColumnType("nvarchar")
                             .HasMaxLength(15)
                       );
    }
}

现在在您的 class 中使用自定义属性:

[Table("foo")]
public class foo {
   …
    [CustomerNo]
    [Display(Name="Customer Identifier")]
    public string CustomerNo {get; set;}
   …
}

[Table("bar")]
public class bar {
   …
    [CustomerNo]
    [Display(Name="Customer Identifier")]
    public string CustomerNo {get; set;}
   …
}

最后,我们必须启用自定义约定,我们可以通过覆盖 DbContext class:

中的 OnModelCreating 来实现
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new CustomerNoConvention());
}

减少多个属性和约定的重复条目的替代解决方案当然是使用继承:

public abstract class HasCustomerNo {
   …
    [CustomerNo]
    [Display(Name="Customer Identifier")]
    public string CustomerNo {get; set;}
   …
}
[Table("foo")]
public class foo : HasCustomerNo  {
   …
    // no need for CustomerNo 
   …
}

[Table("bar")]
public class bar : HasCustomerNo {
   …
    // no need for CustomerNo 
   …
}