如何为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("表名称")”的形式。

测试代码是成功的,不过有时会出现表无法删除的情况,望大伙儿可以指教一二,谢谢!

如果有其它想法或者意见,也请及时反馈,谢谢!

posted @ 2012-07-06 15:17  Serviceboy  阅读(1226)  评论(0编辑  收藏  举报