在上篇文章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());
兩次運行結果如圖: