天天看點

實作UniqueAttribute唯一性限制-優化版

我之前就寫過一篇《實作UniqueAttribute唯一性限制》,雖然實作了通過調用IsValid方法可以進行唯一性驗證,但有一個缺點,那就是耦合度過高,原因是裡面的DB上下文對象是直接寫在裡面的,如下:

public override Boolean IsValid(Object value)
        {
            bool validResult = false;
            //TEMSContext 是我項目中的DB上下文類,這裡是直接指定的,與TEMSContext 緊耦合,若需要實體與通路分離就會有問題!
            using (TEMSContext context = new TEMSContext())
            {
               string sqlCmd=string.Format("select count(1) from [{0}] where [{1}]=@p0",tableName,filedName);
               context.Database.Connection.Open();
               var cmd=context.Database.Connection.CreateCommand();
               cmd.CommandText = sqlCmd;
               var p0 = cmd.CreateParameter();
               p0.ParameterName = "@p0";
               p0.Value = value;
               cmd.Parameters.Add(p0);
               int result=Convert.ToInt32(cmd.ExecuteScalar());
                validResult=(result<=0);
            }
            return validResult;
        }
      

現在為了解決這個問題,我目前采用的是通過屬性注入DB上下文類型,然後再采取反射動态建立執行個體,這樣就降低了依賴,完整代碼如下:

using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace Zwj.TEMS.Common
{
    /// <summary>
    /// 唯一性辨別
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class UniqueAttribute : ValidationAttribute
    {
        protected string tableName;
        protected string filedName;

        public Type ContextType { private get; set; }

        public UniqueAttribute(string tableName, string filedName)
        {
            this.tableName = tableName;
            this.filedName = filedName;
        }

        public override Boolean IsValid(Object value)
        {
            //如果是系統調用,就不會傳入ContextType類型,是以此處就直接傳回通過
            if (this.ContextType == null)
            {
                return true;
            }

            bool validResult = false;
           using (DbContext context = ContextType.Assembly.CreateInstance(ContextType.FullName) as DbContext)
            {
                string sqlCmd = string.Format("select count(1) from [{0}] where [{1}]=@p0", tableName, filedName);
                context.Database.Connection.Open();
                var cmd = context.Database.Connection.CreateCommand();
                cmd.CommandText = sqlCmd;
                var p0 = cmd.CreateParameter();
                p0.ParameterName = "@p0";
                p0.Value = value;
                cmd.Parameters.Add(p0);
                int result = Convert.ToInt32(cmd.ExecuteScalar());
                validResult = (result <= 0);
                context.Database.Connection.Close();
            }
            return validResult;
        }
    }
}
      

這樣雖然降低了對實際DB上下文的依賴,但新問題又出來了,就是如果通過手動來驗證該特性(如下代碼)就沒有問題,但如是想讓EF架構在RUD時能自動驗證,則會出現問題,因為沒有傳入ContextType,是以也就無法進行驗證,我想這也是微軟之是以沒有實作UniqueAttribute唯一性限制的原因吧,不知道哪位高手能指點一下,非常感謝!

以下是手動來驗證該特性:

public void ValidateEntity(object entity)
        {
            var t = entity.GetType();
            var properties = t.GetProperties();
            foreach (var p in properties)
            {
                UniqueAttribute[] attrs;
                if (p.TryGetAttribute<UniqueAttribute>(out attrs))
                {
                    attrs[0].ContextType=typeof(TEMSContext);
                    bool result = attrs[0].IsValid(p.GetValue(entity, null));
                    Assert.IsTrue(result, "驗證不唯一,存在重複值!");
                }
            }
        }
      

  

c# EF