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

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

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

你可能感兴趣的:(framework)