标签:完整 count new 取值 字符 sys ann context sid
大部分人不能将核心运行时(System.Web 中的类)和 ASP.NET Web Forms 应用程序平台(System.Web.UI 中的类)区分开来。
ASP.NET 开发团队在简单的核心运行时抽象之上创建了复杂的 Web Form 抽象和 ASP.NET MVC。正因为 ASP.NET MVC 框架建立在公共抽象之上,所以 ASP.NET MVC 框架能实现的任何功能,任何人也都可以实现。ASP.NET MVC 框架本身也由若干层抽象组成,从而使得开发人员能够选择他们需要的 MVC 片段,替换或修改他们不需要的片段。对于后续的每一个版本,ASP.NET MVC 团队都开放了更多的框架内部定制点。
ASP.NET MVC 4 中的模型系统包括几个可扩展部分,其中包括使用元数据描述模型、验证模型以及影响从请求中构造模型的能力。
将请求数据(表单数据、查询字符串数据、路由信息)转换为模型的过程称为模型绑定。模型绑定分为两个阶段:
真实模型绑定过程中使用的值都来自值提供器。值提供器的作用仅仅是访问能够在模型绑定过程中正确使用的信息。ASP.NET MVC 框架自带的若干值提供器可以提供以下数据源中的数据:
值提供器来自值提供器工厂,并且系统按照值提供器的注册顺序来从中搜寻数据(上面是默认顺序)。开发人员可以编写自己的值提供器工厂和值提供器,并且还可以把它们插入到包含在 ValueProviderFactories.Factories 中的工厂列表中。当模型绑定期间需要使用额外的数据源时,开发人员通常会选择编写自己的值提供器工厂和值提供器。
除了 ASP.NET MVC 本身包含的值提供器工厂以外,开发团队也在 ASP.NET MVC Futures 中包含了另一些值提供器工厂和值提供器:
模型扩展的另一部分是模型绑定器。它们从值提供器系统中获取值,并利用获取的值创建新模型或者填充已有模型。ASP.NET MVC 中的默认模型绑定器(DefaultModelBinder)是一段非常强大的代码,它可以对传统类、集合类、列表、数组、字典进行模型绑定。但默认模型绑定器不支持不可变对象(对象初始值通过构造函数设置,之后不能改变)。
例如,由于 CLR 中 Point 类是不可变的,因此我们必须使用它的值构造一个新实例:
public class PointModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var valueProvider = bindingContext.ValueProvider;
int x = (int)valueProvider.GetValue("X").ConvertTo(typeof(int));
int y = (int)valueProvider.GetValue("Y").ConvertTo(typeof(int));
return new Point(x, y);
}
}
当创建一个新的模型绑定器时,需要告知 MVC 框架存在一个新的模型绑定器(可以在 ModelBinders.Binders 的全局列表中注册新的模型绑定器)以及何时使用它(可用 [ModelBinder] 特性来装饰绑定类)。
模型绑定器往往容易被忽略的一个责任是:验证它们要绑定的值。上面的示例代码未包含任何验证逻辑,因此看上去非常简单。下面是一个更完整的模型绑定器版本:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// We first attempt to find values based on the model name, and if we can‘t find
// anything for the model name, we‘ll fall back to the empty prefix as appropriate.
if (!String.IsNullOrEmpty(bindingContext.ModelName) &&
!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
{
if (!bindingContext.FallbackToEmptyPrefix)
return null;
bindingContext = new ModelBindingContext
{
ModelMetadata = bindingContext.ModelMetadata,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
}
// We have to create a new model, because Point is immutable. When the type
// isn‘t immutable, we can always take in the existing object, when one exists,
// and update it instead. The existing object, if one exists, is available
// via bindingContext.Model. Instead, we‘ll put a temporary (empty) object into
// the binding context so that validation can succeed while we validate all
// the parameter values.
bindingContext.ModelMetadata.Model = new Point();
return new Point(
Get<int>(controllerContext, bindingContext, "X"),
Get<int>(controllerContext, bindingContext, "Y")
);
}
上述代码新增了 2 项内容:
下面是 Get 方法的完整函数:
private TModel Get<TModel>(ControllerContext controllerContext,
ModelBindingContext bindingContext,
string name)
{
// Get the fully qualified name, because model state needs to use that, and not just
// the simple property name.
string fullName = name;
if (!String.IsNullOrWhiteSpace(bindingContext.ModelName))
fullName = bindingContext.ModelName + "." + name;
// Get the value from the value provider
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(fullName);
// Add the attempted value to model state, so that we can round-trip their
// value even when it‘s incorrect and incapable of being held inside the
// model itself (i.e., the user types "abc" for an int).
ModelState modelState = new ModelState { Value = valueProviderResult };
bindingContext.ModelState.Add(fullName, modelState);
// Get the ModelMetadata that represents this property, as we use several of its
// values, and it‘s necessary for validation
ModelMetadata metadata = bindingContext.PropertyMetadata[name];
// Convert the attempted value to null automatically
string attemptedValue = valueProviderResult.AttemptedValue;
if (metadata.ConvertEmptyStringToNull && String.IsNullOrWhiteSpace(attemptedValue))
attemptedValue = null;
TModel model;
bool invalidValue = false;
try
{
// Attempt to convert the value to the correct type
model = (TModel)valueProviderResult.ConvertTo(typeof(TModel));
metadata.Model = model;
}
catch (Exception)
{
// Conversion failed, so give back the default value for the type
// and set the attempted value into model metadata
model = default(TModel);
metadata.Model = attemptedValue;
invalidValue = true;
}
// Run the validators for the given property
IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(metadata, controllerContext);
foreach (var validator in validators)
foreach (var validatorResult in validator.Validate(bindingContext.Model))
modelState.Errors.Add(validatorResult.Message);
// Only add the "invalid value" message if there were no other errors, because things like
// required validation should trump conversion failures, and null/empty values will often
// fail both required validation and type-conversion validation
if (invalidValue && modelState.Errors.Count == 0)
modelState.Errors.Add(
String.Format(
"The value ‘{0}‘ is not a valid value for {1}.",
attemptedValue,
metadata.GetDisplayName()
)
);
return model;
}
扩展 ASP.NET MVC 模型扩展 – ASP.NET MVC 4 系列
标签:完整 count new 取值 字符 sys ann context sid
原文地址:http://www.cnblogs.com/SkySoot/p/6050251.html