asp.net mvc源码分析-Controller篇 ValueProvider
在上篇文章asp.net mvc源码分析-Action篇 IModelBinder中我们提到了ValueProvider,其实这个东西是Controller的属性,在写前面Controller的是曾打算把它写书来,后来有以下在后面用的时候在写它相关的东东吧,需求才能推动发展啊。先说明一下这个属性石很重要的,Action参数的值最总都是通过它来获取的。
public IValueProvider ValueProvider {
get {
if (_valueProvider == null) {
_valueProvider = ValueProviderFactories.Factories.GetValueProvider(ControllerContext);
}
return _valueProvider;
}
set {
_valueProvider = value;
}
}
想让我们看看ValueProviderFactories类是个什么东东
public static class ValueProviderFactories {
private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection() {
new ChildActionValueProviderFactory(),
new FormValueProviderFactory(),
new JsonValueProviderFactory(),
new RouteDataValueProviderFactory(),
new QueryStringValueProviderFactory(),
new HttpFileCollectionValueProviderFactory(),
};
public static ValueProviderFactoryCollection Factories {
get {
return _factories;
}
}
}
看来 默认就跟我们加了6个ProviderFactory啊,这个顺序很重要。
public IValueProvider GetValueProvider(ControllerContext controllerContext) {
var valueProviders = from factory in _serviceResolver.Current
let valueProvider = factory.GetValueProvider(controllerContext)
where valueProvider != null
select valueProvider;
return new ValueProviderCollection(valueProviders.ToList());
}
GetValueProvider这个返回的是一个ValueProvider集合类ValueProviderCollection,所以我认为这个方法名应该加个s改为GetValueProviders。这里的_serviceResolver.Current就是我们默认的那6个ProviderFactory
其中 除JsonValueProviderFactory 有点特殊,其他的几个只要controllerContext有效都能返回valueProvider ,他们的valueProvider 类型依次对应如下:
ChildActionValueProviderFactory ->ChildActionValueProvider
FormValueProviderFactory ->FormValueProvider
RouteDataValueProviderFactory ->RouteDataValueProvider
QueryStringValueProviderFactory ->QueryStringValueProvider
HttpFileCollectionValueProviderFactory->HttpFileCollectionValueProvider
而 JsonValueProviderFactory 除了要验证controllerContext数据是否有效还需要验证 json格式是否正确,所以它直接返回了一个类DictionaryValueProvider<object>。
这里的每个ValueProvider实例类我们就不用管了,太细节了。
现在我们就可以得到一个ValueProviderCollection,它有一个很有用的方法
public virtual ValueProviderResult GetValue(string key) {
return GetValue(key, skipValidation: false);
}
public virtual ValueProviderResult GetValue(string key, bool skipValidation) {
return (from provider in this
let result = GetValueFromProvider(provider, key, skipValidation)
where result != null
select result).FirstOrDefault();
}
从先前得到的ValueProvider依次待用他们的GetValue方法,返回了一个不含ValueProviderResult为空的ValueProviderResult集合,最总返回这个集合中的第一个ValueProviderResult。
internal static ValueProviderResult GetValueFromProvider(IValueProvider provider, string key, bool skipValidation) {
// Since IUnvalidatedValueProvider is a superset of IValueProvider, it's always OK to use the
// IUnvalidatedValueProvider-supplied members if they're present. Otherwise just call the
// normal IValueProvider members.
IUnvalidatedValueProvider unvalidatedProvider = provider as IUnvalidatedValueProvider;
return (unvalidatedProvider != null) ? unvalidatedProvider.GetValue(key, skipValidation) : provider.GetValue(key);
}
为什么会出现上面provider as IUnvalidatedValueProvider这样的代码了?因为前面提到的
QueryStringValueProvider、NameValueCollectionValueProvider都继承与NameValueCollectionValueProvider
public class NameValueCollectionValueProvider : IValueProvider, IUnvalidatedValueProvider
而ChildActionValueProvider、RouteDataValueProvider、HttpFileCollectionValueProvider却继承与DictionaryValueProvider
public class DictionaryValueProvider<TValue> : IValueProvider
NameValueCollectionValueProvider的主要方法如下:
private void AddValues(NameValueCollection validatedCollection, NameValueCollection unvalidatedCollection, CultureInfo culture) {
// Need to read keys from the unvalidated collection, as M.W.I's granular request validation is a bit touchy
// and validated entries at the time the key or value is looked at. For example, GetKey() will throw if the
// value fails request validation, even though the value's not being looked at (M.W.I can't tell the difference).
if (unvalidatedCollection.Count > 0) {
_prefixes.Add("");
}
foreach (string key in unvalidatedCollection) {
if (key != null) {
_prefixes.UnionWith(ValueProviderUtil.GetPrefixes(key));
// need to look up values lazily, as eagerly looking at the collection might trigger validation
_values[key] = new ValueProviderResultPlaceholder(key, validatedCollection, unvalidatedCollection, culture);
}
}
}
public virtual ValueProviderResult GetValue(string key, bool skipValidation) {
if (key == null) {
throw new ArgumentNullException("key");
}
ValueProviderResultPlaceholder placeholder;
_values.TryGetValue(key, out placeholder);
if (placeholder == null) {
return null;
}
else {
return (skipValidation) ? placeholder.UnvalidatedResult : placeholder.ValidatedResult;
}
}
而DictionaryValueProvider的主要方法:
private void AddValues(IDictionary<string, TValue> dictionary, CultureInfo culture) {
if (dictionary.Count > 0) {
_prefixes.Add("");
}
foreach (var entry in dictionary) {
_prefixes.UnionWith(ValueProviderUtil.GetPrefixes(entry.Key));
object rawValue = entry.Value;
string attemptedValue = Convert.ToString(rawValue, culture);
_values[entry.Key] = new ValueProviderResult(rawValue, attemptedValue, culture);
}
}
public virtual ValueProviderResult GetValue(string key) {
if (key == null) {
throw new ArgumentNullException("key");
}
ValueProviderResult vpResult;
_values.TryGetValue(key, out vpResult);
return vpResult;
}
充这里可以看到ValueProviderResultPlaceholder是一个ValueProviderResult的包装类,使其数据实现延迟加载,
private sealed class ValueProviderResultPlaceholder {
private readonly Lazy<ValueProviderResult> _validatedResultPlaceholder;
private readonly Lazy<ValueProviderResult> _unvalidatedResultPlaceholder;
public ValueProviderResultPlaceholder(string key, NameValueCollection validatedCollection, NameValueCollection unvalidatedCollection, CultureInfo culture) {
_validatedResultPlaceholder = new Lazy<ValueProviderResult>(() => GetResultFromCollection(key, validatedCollection, culture), LazyThreadSafetyMode.None);
_unvalidatedResultPlaceholder = new Lazy<ValueProviderResult>(() => GetResultFromCollection(key, unvalidatedCollection, culture), LazyThreadSafetyMode.None);
}
private static ValueProviderResult GetResultFromCollection(string key, NameValueCollection collection, CultureInfo culture) {
string[] rawValue = collection.GetValues(key);
string attemptedValue = collection[key];
return new ValueProviderResult(rawValue, attemptedValue, culture);
}
public ValueProviderResult ValidatedResult {
get { return _validatedResultPlaceholder.Value; }
}
public ValueProviderResult UnvalidatedResult {
get { return _unvalidatedResultPlaceholder.Value; }
}
}
具体其他的什么细节之处我就不提了。在项目中我们往往要实现自己ValueProviderFactory,那么我们需要怎么注册它了在 Application_Start()加入ValueProviderFactories.Factories.Add(xxxx)。
我们举一个demo来说说怎么用的吧
public class CookieValueProviderFactory : ValueProviderFactory
{
public CookieValueProviderFactory() { }
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
return new CookieValueProvider(controllerContext);
}
}
public class CookieValueProvider : NameValueCollectionValueProvider
{
public CookieValueProvider(ControllerContext controllerContext) :
base(GetCookies(controllerContext), CultureInfo.InvariantCulture)
{ }
static NameValueCollection GetCookies(ControllerContext controllerContext)
{
NameValueCollection data = new NameValueCollection();
foreach (string key in controllerContext.HttpContext.Request.Cookies.AllKeys)
{
data.Add(key, controllerContext.HttpContext.Request.Cookies[key].Value);
}
return data;
}
}
public class HomeController : Controller
{
public ActionResult Index(string name)
{
if (string.IsNullOrEmpty(name))
{
Response.Cookies.Add(new HttpCookie("name", "majiang"));
return Content("写入cookie");
}
else
{
return Content("读取cookie:"+name);
}
}
}
在Application_Start中加入ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
两次运行结果如图:

浙公网安备 33010602011771号