在Suteki.Shop中,作者構造了一個ModelBinder基類“DataBinder”,其本身繼承自 IModelBinder接口,並以此其類派生出其它一些子類類如ProductBinder等等。可以說除了極個別的地方 之外,DataBinder被用於了Suteki.Shop大多數的ModelBinder綁定場景之路。
首先看一下其類圖 結構:
作為基類, DataBinder(圖中左下方)實現了將HTTP請求過來的數據轉換成為模型中相應的類型。其核心方法就是 BindModel,下面做一下解釋說明:
public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object entity;
if (declaringAttribute == null || declaringAttribute.Fetch)
{
entity = FetchEntity(bindingContext, controllerContext);
}
else
{
entity = Activator.CreateInstance(bindingContext.ModelType);
}
try
{
validatingBinder.UpdateFrom(entity, controllerContext.HttpContext.Request.Form, bindingContext.ModelState, bindingContext.ModelName);
}
catch(ValidationException ex)
{
//Ignore validation exceptions - they are stored in ModelState.
//The controller can access the errors by inspecting the ModelState dictionary.
}
return entity;
}
首先要說明的就是declaringAttribute,這個變 量其實是DataBindAttribute類型實例,其作用是判斷當前所請求過來的數據是用於創建新的Model信息 還是獲取並綁定已有的數據記錄。該實例的實始化是交給Accept方法來實現的(注意Accept方法的聲明 是在IAcceptsAttribute接口中定義的):
public void Accept(Attribute attribute)
{
declaringAttribute = (DataBindAttribute) attribute;
}
而對該方法的調用是放在了 BindUsingAttribute的GetBinder()方法中,所以這裡要先看一下BindUsingAttribute的具體實現:
public class BindUsingAttribute : CustomModelBinderAttribute
{
private readonly Type binderType;
public BindUsingAttribute(Type binderType)
{
if(!typeof(IModelBinder).IsAssignableFrom(binderType))
{
throw new InvalidOperationException("Type '{0}' does not implement IModelBinder.".With(binderType.Name));
}
this.binderType = binderType;
}
public override IModelBinder GetBinder()
{
var binder = (IModelBinder) ServiceLocator.Current.GetInstance(binderType);
if (binder is IAcceptsAttribute)
{
((IAcceptsAttribute)binder).Accept(this);
}
return binder;
}
}
這個屬性類也就是在Action中進行 ModelBinder綁定時用到的,其類構造方法中通過一個類型參數初始化並在GetBinder()方法中使用 ServiceLocator來進行最終的ModelBinder實例化構建,注意在該方法中有對當前binder的邏輯判斷 (IAcceptsAttribute),並以此來區別當前Action綁定的ModelBinder是否實現了IAcceptsAttribute接口 。如果沒實現或declaringAttribute.Fetch為true時,就會在之前的DataBinder類方法 “BindModel”中調用“FetchEntity()”方法,FetchEntity會從提交的數據中的主鍵(PrimaryKey)中檢索數據並返回該對象實例,代碼如下:
private object FetchEntity(ModelBindingContext bindingContext, ControllerContext controllerContext)
{
object entity;
var primaryKey = bindingContext.ModelType.GetPrimaryKey();//從Shop.dbml中查找相應的主鍵信息
string name = bindingContext.ModelName + "." + primaryKey.Name;//拼接出要獲取的主 鍵名稱
string rawKeyValue = controllerContext.HttpContext.Request.Form [name];
if (string.IsNullOrEmpty(rawKeyValue))
{
throw new InvalidOperationException("Could not find a value named '{0} '".With(name));
}
int key = Convert.ToInt32(rawKeyValue);
var repository = resolver.GetRepository(bindingContext.ModelType);
entity = repository.GetById(key);
return entity;
}
注意上面代碼中的這兩行:
var repository = resolver.GetRepository(bindingContext.ModelType);
entity = repository.GetById(key);