我之前就寫過一篇《實作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, "驗證不唯一,存在重複值!");
}
}
}