我想减少为在多个对象和关联的数据库表中使用的对象属性指定相同属性的重复代码。
我正在使用属性属性来定义应如何将属性保存在数据库中以及应如何在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;}
…
}
我想做的是创建一个自定义属性,将上述属性组合成一个类似[CustomerNoAttribute]的属性(我知道可以省略后缀“ Attribute”,以减少与CustomerNo类的混淆)。
没有多重继承,所以我不能仅从ColumnAttribute,MaxLengthAttribute和DisplayAttribute继承。
有没有一种方法可以使用合成来完成这项工作?例如
下面的这段代码不起作用。新的内部属性未附加到我放置[CustomerNoAttribute]的属性上。
public CustomerNoAttribute: Attribute {
public CustomerNoAttribute() {
new MaxLengthAttribute(15);
new DisplayAttribute().Name = "Customer Identifier";
new ColumnAttribute().TypeName = "nvarchar";
}
}
还有另一种减少重复的方法吗?
使用属性添加运行时的技术无济于事,因为看起来实体框架的代码优先迁移仅关注编译时属性。
这里的解决方案相对简单,是我最喜欢的实体框架功能之一:
代码优先约定
See Custom Code First Conventions为完整运行,概念是您可以定义自己的任意规则或EF运行时应遵守的conventions,这可能基于attributes,但并非如此不得不。如果您确实需要,可以根据字段名称的后缀或前缀创建约定。
Custom Conventions与此解决方案https://stackoverflow.com/a/38504552/1690217中说明的自定义类型描述符相似,除了专门针对实体框架代码优先模型的机制
您走在正确的轨道上,制作自定义属性可以简化自定义代码约定的实现,但是Display属性是有问题的...通常,我建议从提供最多配置的属性(在这种情况下为DisplayAttribute
)继承,但是我们无法继承该类型,因为它是密封的。不幸的是,由于在消费者端可以采用不同的约定概念,因此我将DisplayAttribute
排除在解决方案之外。相反,它显示了如何使用自定义属性来替换多个基于DataAnnotation
的Attributes。
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)
);
}
}
现在可以在您的课程中使用自定义属性:
[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;}
…
}
最后,我们必须启用自定义约定,我们可以通过在您的OnModelCreating
类中覆盖DbContext
来做到这一点:
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
…
}