Welcome 微信登录

首页 / 网页编程 / ASP.NET / Asp.net MVC示例项目“Suteki.Shop”分析之ModelBinder

Asp.net MVC示例项目“Suteki.Shop”分析之ModelBinder2009-12-31在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);