如何为EntityFramework添加Unique功能?
直到目前为止,EntityFramework似乎没有UniqueAttribute这个属性,因而也就无法为数据模型表某个特定字段指定是Unique。那么怎么办呢?我们就DIY一次吧!
首先自己写一个特性,该特性只能被用于property上,由于只是一个标记特性而以,因此类体为空——
[C#]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class UniqueKeyAttribute : Attribute { }
[VB.NET]
<AttributeUsage(AttributeTargets.[Property], AllowMultiple := False, Inherited := True)> _ Public Class UniqueKeyAttribute Inherits Attribute End Class
然后自己实现IDatabaseInitializer接口。那是因为EntityFramework的DataBase有一个静态方法叫做SetInitialize<T>,其中“T”是预备生成的模型类(继承DbContext),而这个方法需要一个实现该接口的类用于初始化数据库。该类实现如下:
[C#]
public class MyDbGenerator<T> : IDatabaseInitializer<T> where T : DbContext { public void InitializeDatabase(T context) { context.Database.Delete(); context.Database.Create(); //获取全部父类的公开属性
var fatherPropertyNames = typeof(DbContext).GetProperties().Select(pi => pi.Name).ToList(); //先剔除由父类继承的公共属性
foreach (PropertyInfo item in typeof(T).GetProperties().Where(p => fatherPropertyNames.IndexOf(p.Name) < 0).Select(p => p)) { //获取子类DbSet<T>中的那个"T"
Type entityModelType = item.PropertyType.GetGenericArguments()[0]; //反射“T”,并且获取已经加上“Unique”的自定义属性字段名称
var allfieldNames = from prop in entityModelType.GetProperties() where prop.GetCustomAttributes(typeof(UniqueKeyAttribute), true).Count() > 0 select prop.Name; //逐个开始用SQL命令生成唯一字段
foreach (string s in allfieldNames) { context.Database.ExecuteSqlCommand("alter table " + entityModelType.Name + " add unique(" + s + ")"); } } } }
[VB.NET]
Public Class MyDbGenerator(Of T As DbContext) Implements IDatabaseInitializer(Of T) Public Sub InitializeDatabase(context As T) context.Database.Delete() context.Database.Create() '获取全部父类的公开属性 Dim fatherPropertyNames = GetType(DbContext).GetProperties().[Select](Function(pi) pi.Name).ToList() '先剔除由父类继承的公共属性 For Each item As PropertyInfo In GetType(T).GetProperties().Where(Function(p) fatherPropertyNames.IndexOf(p.Name) < 0).[Select](Function(p) p) '获取子类DbSet<T>中的那个"T" Dim entityModelType As Type = item.PropertyType.GetGenericArguments()(0) '反射“T”,并且获取已经加上“Unique”的自定义属性字段名称 Dim allfieldNames = From prop In entityModelType.GetProperties() Where prop.GetCustomAttributes(GetType(UniqueKeyAttribute), True).Count() > 0prop.Name '逐个开始用SQL命令生成唯一字段 For Each s As String In allfieldNames context.Database.ExecuteSqlCommand("alter table " + entityModelType.Name & " add unique(" & s & ")") Next Next End Sub End Class
下面给出测试:
[C#]
public class Category { [Key] public int Id { get; set; } [UniqueKey] public int IdentifyCode { get; set; } public string Name { get; set; } } public class DbG : DbContext { protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } public DbSet<Category> Categories { get; set; } } public class MainTest { static void Main(string[] args) { Database.SetInitializer<DbG>(new MyDbGenerator<DbG>()); using (DbG g = new DbG()) { g.Categories.Add(new Category { Id = 1, Name = "Category" }); g.SaveChanges(); Console.WriteLine("OK"); } } }
[VB.NET]
copy to clipboard | view source | convert again Public Class Category <Key> _ Public Property Id() As Integer Get Return m_Id End Get Set m_Id = Value End Set End Property Private m_Id As Integer <UniqueKey> _ Public Property IdentifyCode() As Integer Get Return m_IdentifyCode End Get Set m_IdentifyCode = Value End Set End Property Private m_IdentifyCode As Integer Public Property Name() As String Get Return m_Name End Get Set m_Name = Value End Set End Property Private m_Name As String End Class Public Class DbG Inherits DbContext Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder) modelBuilder.Conventions.Remove(Of PluralizingTableNameConvention)() End Sub Public Property Categories() As DbSet(Of Category) Get Return m_Categories End Get Set m_Categories = Value End Set End Property Private m_Categories As DbSet(Of Category) End Class Public Class MainTest Private Shared Sub Main(args As String()) Database.SetInitializer(Of DbG)(New MyDbGenerator(Of DbG)()) Using g As New DbG() g.Categories.Add(New Category() With { _ Key .Id = 1, _ Key .Name = "Category" _ }) g.SaveChanges() Console.WriteLine("OK") End Using End Sub End Class
注意:此处的“OnModelCreating”方法必须被重写。因为默认情况下Code-First生成的数据表名总是“实体模型名”(复数)形式,而我的代码目前只考虑了(直接根据模型名)生成表(也就是Add unique这部分)。因此必须取消表的“自动复数化”形式。除了我的这个方法以外,其实还可以采用在模型类上冠以“Table("表名称")”的形式。
测试代码是成功的,不过有时会出现表无法删除的情况,望大伙儿可以指教一二,谢谢!
如果有其它想法或者意见,也请及时反馈,谢谢!