能否创建一个C#属性,该属性是Entity Framework Code首次迁移所采用的其他属性的组合

Mik*_*zyk 6 c# attributes entity-framework ef-migrations

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

我正在使用属性属性来定义属性应如何保存在数据库中以及应如何在UI元素中命名。就我而言,此属性出现在多个表/对象中,我希望它在所有位置都具有相同的属性。我还希望这些属性能被实体框架的代码优先迁移所吸收。看起来像代码优先迁移遍历属性,并寻找诸如MaxLengthAttribute的特定类或从特定类继承的类。太糟糕了,Entity Framework不查找接口。

我不想将此字符串移动到另一个表,因为将使用这些表的客户希望直接通过“ 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;}
   …
}
Run Code Online (Sandbox Code Playgroud)

我想做的是创建一个自定义属性,将上述属性组合成一个类似[CustomerNoAttribute]的属性(我知道我可以省略后缀“ Attribute”,以减少类CustomerNo的混淆)。

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

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

下面的代码不起作用。新的内部属性未附加到我放置[CustomerNoAttribute]的属性上。

public CustomerNoAttribute: Attribute {

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

还有另一种方法可以减少这种重复吗?

使用运行时属性添加的技术无济于事,因为看起来实体框架的代码优先迁移仅关注编译时属性。

Chr*_*ler 2

这里的解决方案相对简单,也是我最喜欢的实体框架功能之一:

\n\n
\n

代码优先约定

\n\n

请参阅自定义代码优先约定以了解完整的运行过程,其概念是您可以定义 EF 运行时应遵守的自己的任意规则或约定,这可能基于属性,但不一定如此。如果您确实需要,可以根据字段名称的后缀或前缀创建约定。

\n\n
\n

自定义约定是与自定义类型描述符类似的机制,如本解决方案/sf/answers/2695318671/中所述,但专门针对实体框架代码优先模型的除外

\n
\n
\n\n

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

\n\n
public CustomerNoAttribute : Attribute {\n}\n\npublic class CustomerNoConvention : Convention\n{\n    public CustomerNoConvention()\n    {\n        this.Properties()\n            .Where(p => p.GetCustomAttributes(false).OfType<CustomerNoAttribute>().Any()\n            .Configure(c => c.HasColumnType("nvarchar")\n                             .HasMaxLength(15)\n                       );\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
[Table("foo")]\npublic class foo {\n   \xe2\x80\xa6\n    [CustomerNo]\n    [Display(Name="Customer Identifier")]\n    public string CustomerNo {get; set;}\n   \xe2\x80\xa6\n}\n\n[Table("bar")]\npublic class bar {\n   \xe2\x80\xa6\n    [CustomerNo]\n    [Display(Name="Customer Identifier")]\n    public string CustomerNo {get; set;}\n   \xe2\x80\xa6\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,我们必须启用自定义约定,我们可以通过OnModelCreating在您的DbContext类中重写来做到这一点:

\n\n
protected override void OnModelCreating(DbModelBuilder modelBuilder)\n{\n    modelBuilder.Conventions.Add(new CustomerNoConvention());\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
public abstract class HasCustomerNo {\n   \xe2\x80\xa6\n    [CustomerNo]\n    [Display(Name="Customer Identifier")]\n    public string CustomerNo {get; set;}\n   \xe2\x80\xa6\n}\n[Table("foo")]\npublic class foo : HasCustomerNo  {\n   \xe2\x80\xa6\n    // no need for CustomerNo \n   \xe2\x80\xa6\n}\n\n[Table("bar")]\npublic class bar : HasCustomerNo {\n   \xe2\x80\xa6\n    // no need for CustomerNo \n   \xe2\x80\xa6\n}\n
Run Code Online (Sandbox Code Playgroud)\n