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());

兩次運行結果如圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM