asp.net mvc源碼分析-Controller篇 ValueProvider

來源:互聯網
上載者:User

在上篇文章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());

兩次運行結果

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.