In Suteki.shop, the author constructs a modelbinder base class "DataBinder", which itself inherits from the Imodelbinder interface and derives other subclass classes such as Productbinder, and so on, with this class. It can be said that, in addition to the very few places, DataBinder was used to suteki.shop most of the modelbinder bound scenes.
First look at its class diagram structure:
As a base class, DataBinder (the lower left of the figure) implements converting the HTTP request data into the corresponding type in the model. The core approach is Bindmodel, and here's how to explain:
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;
}
The first thing to explain is declaringattribute, a variable that is actually a Databindattribute type instance, and its role is to determine whether the currently requested data is used to create new model information or to acquire and bind existing data records. The actual initialization of the instance is given to the Accept method (note that the declaration of the Accept method is defined in the Iacceptsattribute interface):
public void Accept(Attribute attribute)
{
declaringAttribute = (DataBindAttribute) attribute;
}
The call to the method is placed in the Bindusingattribute Getbinder () method, so here's a look at the specific implementation of 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;
}
}
This property class is used for modelbinder binding in action, and its class constructor is initialized with a type parameter and used in the Getbinder () method Servicelocator to perform the final Modelbinder instantiation, note that in this method there is a logical judgment of the current binder (Iacceptsattribute), and to distinguish whether the modelbinder of the current action binding implements the Iacceptsattribute interface. If not implemented or Declaringattribute.fetch is true, the Fetchentity () method is invoked in the previous DataBinder class method "Bindmodel". Fetchentity retrieves the data from the primary key (PrimaryKey) in the submitted data and returns the object instance with the following code:
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;
}
Notice the two lines in the above code:
var repository = resolver.GetRepository(bindingContext.ModelType);
entity = repository.GetById(key);