あなたをオープンソースに連れて行きます-ASP.NET_MVC(十三)
7352 ワード
今日は前編に引き続きModelBindingについて議論します.
接続をスムーズにするために、前編の最後のコードをもう一度貼り付けて、一応コードセグメント1と呼びましょう.
コードセグメント1
ここで重要なのは、MVCがIModelBinderインタフェースのインスタンスをどのように取得するか、2つ目は、そのインタフェースのBindModelメソッドがRequest関連情報を対応するModelにバインドする方法です.
まず第1点を見て、コードセグメント1でGetModelBinderメソッドの呼び出しを見つけ、F 12を押してその定義(コードセグメント2)に入る.この方法はただ1つの文で、「//look on the parameter itself,then look in the global table」という注釈を組み合わせると、MVCが先に伝わってきたparameterDescriptorをチェックすることがわかります.BindingInfo.Binderが空であるかどうかは、空でなければその属性を直接返し、空であればBinder sを呼び出す.GetBinderメソッドは、グローバルテーブルでIModelBinderを探します.
コードセグメント2
Binders.GetBinderメソッドの定義は、多くのリロードがあり、最終呼び出しのバージョン(コードセグメント3)を直接見つけます.
コードセグメント3
マイクロソフトのオープンソースプロジェクトグループはコード注釈を重視しています.ほら、この方法は最初はかなり整った注釈です.皆さんの英語はかなりのNBですが、私はやはり恥をかいて翻訳しましょう.
このタイプのbinderを探してみます.次の順序で検索します.
1、providerが返すBinder
2、グローバルテーブルに登録したBinder
3、このタイプで定義されたBinder特性
4、ユーザー提供の代替Binder
以下、この4つの実現原理を1つずつ説明する.
1、providerが返すBinder.
次の文を見つけます.
IModelBinderbinder = _modelBinderProviders.GetBinder(modelType);
ModelBinderProvidersというプライベートメンバーがどのように割り当てられているかを見て、そのクラスModelBinderDictionaryのコンストラクション関数(コードセグメント4)を見つけます.
コードセグメント4
modelBinderProvidersはModelBinderProvidersを用いることがわかる.BinderProvidersが割り当てたもので、ModelBinderProviders.BinderProviders【1】とは何か.コードセグメント5を見てみましょう.これは私たちが自分のMVCプロジェクトでGlobalです.asaxファイルのApplication_Startメソッドでは、2番目の文(/*で)は、カスタムmModelBinderProviderをModelBinderProvidersに追加します.BinderProviders集合では、この集合が上の「1」の何かです.
コードセグメント5
2、グローバルテーブルに登録されているBinder.
これは比較的簡単で、innerDictionary.TryGetValue(modelType,out binder)』というコードです.innerDictionaryは辞書のオブジェクトで、この辞書でこのタイプのBinderを探すことを意味します.
3、このタイプで定義されたBinder特性.
コードセグメント3でModelBindersを見つける.GetBinderFromAttributesメソッドは,名前からその役割がAttributesからBinderを取得することであると推測できる.次に、その定義(コードセグメント6)に進む.このコードは、まずGetAttributes()によってタイプtypeのすべての特性(Attributes)を取得し、SingleOfTypeDefaultOrErrorメソッドを呼び出してユーザ定義のCustomModelBinderAttributeを取得し、最後にGetBinderメソッドを呼び出してIModelBinderを取得して返す.
コードセグメント6
SingleOfTypeDefaultOrErrorメソッドの定義は、IList汎用クラスの拡張メソッドであり、1つのIListセットからタイプがTMatchである要素を検索し、セットに1つの一致しかない場合、その要素を返す役割を果たすコードセグメント7を参照する.一致しない場合は、空を返します.複数の一致がある場合、コールバック依頼errorActionが呼び出されます.
コードセグメント7
接続をスムーズにするために、前編の最後のコードをもう一度貼り付けて、一応コードセグメント1と呼びましょう.
protected virtual objectGetParameterValue(ControllerContext controllerContext, ParameterDescriptorparameterDescriptor)
{
// collect all of the necessarybinding properties
Type parameterType =parameterDescriptor.ParameterType;
IModelBinder binder =GetModelBinder(parameterDescriptor);
IValueProvider valueProvider =controllerContext.Controller.ValueProvider;
string parameterName =parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
Predicate<string>propertyFilter = GetPropertyFilter(parameterDescriptor);
// finally, call into the binder
ModelBindingContext bindingContext= new ModelBindingContext()
{
FallbackToEmptyPrefix =(parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefixnot specified
ModelMetadata =ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
ModelName = parameterName,
ModelState =controllerContext.Controller.ViewData.ModelState,
PropertyFilter =propertyFilter,
ValueProvider = valueProvider
};
object result =binder.BindModel(controllerContext, bindingContext);
return result ??parameterDescriptor.DefaultValue;
}
コードセグメント1
ここで重要なのは、MVCがIModelBinderインタフェースのインスタンスをどのように取得するか、2つ目は、そのインタフェースのBindModelメソッドがRequest関連情報を対応するModelにバインドする方法です.
まず第1点を見て、コードセグメント1でGetModelBinderメソッドの呼び出しを見つけ、F 12を押してその定義(コードセグメント2)に入る.この方法はただ1つの文で、「//look on the parameter itself,then look in the global table」という注釈を組み合わせると、MVCが先に伝わってきたparameterDescriptorをチェックすることがわかります.BindingInfo.Binderが空であるかどうかは、空でなければその属性を直接返し、空であればBinder sを呼び出す.GetBinderメソッドは、グローバルテーブルでIModelBinderを探します.
private IModelBinderGetModelBinder(ParameterDescriptor parameterDescriptor)
{
// look on the parameter itself,then look in the global table
returnparameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
}
コードセグメント2
Binders.GetBinderメソッドの定義は、多くのリロードがあり、最終呼び出しのバージョン(コードセグメント3)を直接見つけます.
private IModelBinder GetBinder(TypemodelType, IModelBinder fallbackBinder)
{
// Try to look up a binder for thistype. We use this order of precedence:
// 1. Binder returned from provider
// 2. Binder registered in theglobal table
// 3. Binder attribute defined onthe type
// 4. Supplied fallback binder
IModelBinder binder =_modelBinderProviders.GetBinder(modelType);
if (binder != null)
{
return binder;
}
if(_innerDictionary.TryGetValue(modelType, out binder))
{
return binder;
}
// Function is called frequently,so ensure the error delegate is stateless
binder =ModelBinders.GetBinderFromAttributes(modelType, (Type errorModel) =>
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,MvcResources.ModelBinderDictionary_MultipleAttributes, errorModel.FullName));
});
return binder ?? fallbackBinder;
}
コードセグメント3
マイクロソフトのオープンソースプロジェクトグループはコード注釈を重視しています.ほら、この方法は最初はかなり整った注釈です.皆さんの英語はかなりのNBですが、私はやはり恥をかいて翻訳しましょう.
このタイプのbinderを探してみます.次の順序で検索します.
1、providerが返すBinder
2、グローバルテーブルに登録したBinder
3、このタイプで定義されたBinder特性
4、ユーザー提供の代替Binder
以下、この4つの実現原理を1つずつ説明する.
1、providerが返すBinder.
次の文を見つけます.
IModelBinderbinder = _modelBinderProviders.GetBinder(modelType);
ModelBinderProvidersというプライベートメンバーがどのように割り当てられているかを見て、そのクラスModelBinderDictionaryのコンストラクション関数(コードセグメント4)を見つけます.
public ModelBinderDictionary()
:this(ModelBinderProviders.BinderProviders)
{
}
internal ModelBinderDictionary(ModelBinderProviderCollectionmodelBinderProviders)
{
_modelBinderProviders =modelBinderProviders;
}
コードセグメント4
modelBinderProvidersはModelBinderProvidersを用いることがわかる.BinderProvidersが割り当てたもので、ModelBinderProviders.BinderProviders【1】とは何か.コードセグメント5を見てみましょう.これは私たちが自分のMVCプロジェクトでGlobalです.asaxファイルのApplication_Startメソッドでは、2番目の文(/*で)は、カスタムmModelBinderProviderをModelBinderProvidersに追加します.BinderProviders集合では、この集合が上の「1」の何かです.
protected voidApplication_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinderProviders.BinderProviders.Add(newCustomModelBinderProvider());//*
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
コードセグメント5
2、グローバルテーブルに登録されているBinder.
これは比較的簡単で、innerDictionary.TryGetValue(modelType,out binder)』というコードです.innerDictionaryは辞書のオブジェクトで、この辞書でこのタイプのBinderを探すことを意味します.
3、このタイプで定義されたBinder特性.
コードセグメント3でModelBindersを見つける.GetBinderFromAttributesメソッドは,名前からその役割がAttributesからBinderを取得することであると推測できる.次に、その定義(コードセグメント6)に進む.このコードは、まずGetAttributes()によってタイプtypeのすべての特性(Attributes)を取得し、SingleOfTypeDefaultOrErrorメソッドを呼び出してユーザ定義のCustomModelBinderAttributeを取得し、最後にGetBinderメソッドを呼び出してIModelBinderを取得して返す.
internal static IModelBinder GetBinderFromAttributes(Type type,Action<Type> errorAction)
{
AttributeList allAttrs = newAttributeList(TypeDescriptorHelper.Get(type).GetAttributes());
CustomModelBinderAttributebinder = allAttrs.SingleOfTypeDefaultOrError<Attribute,CustomModelBinderAttribute, Type>(errorAction, type);
return binder == null ? null : binder.GetBinder();
}
コードセグメント6
SingleOfTypeDefaultOrErrorメソッドの定義は、IList
/// <summary>
/// Returns a single value in list matching type TMatch if there is onlyone, null if there are none of type TMatch or calls the
/// errorAction with errorArg1 if there is more than one.
/// </summary>
public static TMatch SingleOfTypeDefaultOrError<TInput, TMatch,TArg1>(this IList<TInput> list, Action<TArg1> errorAction, TArg1errorArg1) where TMatch : class
{
Contract.Assert(list != null);
Contract.Assert(errorAction != null);
TMatch result = null;
for (int i = 0; i < list.Count; i++)
{
TMatch typedValue = list[i] asTMatch;
if (typedValue != null)
{
if (result == null)
{
result = typedValue;
}
else
{
errorAction(errorArg1);
return null;
}
}
}
return result;
}
コードセグメント7