最近MVCを勉强したことについて
33724 ワード
良いプログラマーは、新しい知識に直面するたびに、どのように深いものをつかむかを考えています.今日はMVCの流れを研究します.
ASPを使っているのでNET MVCが開発されたとき、私たちが最初に触れたのはルーティングであり、伝説のSystemでもある.Web.Routing.RouteTable,Reflectionクラスのコードは次のとおりです.
MvcRouteHandlerオブジェクトがコンストラクション関数に渡されるRouteオブジェクトが作成されました.新しいRouteオブジェクトのDefault、Constraints、DataTokensの3つのRouteValueDictionaryタイプのプロパティが設定されています.デフォルト値(Defaults)とコンストレイント(Constraints)はobjectのオブジェクトによって初期化されています.RouteValueDictionaryタイプについてはstringをキーobjectとする値の辞書です.具体的なコードはここでは貼らないので、反射してみてください.RouteValueDictionaryというコンストラクション関数とプライベートAddValuesメソッドを見てみましょう.によって所望のIControllerFactoryサービスを解析し、そのうち2つのリロードされたSetControllerFactory注入メソッドの実現は比較的簡単である.ただ最初はなぜ注入されたIControllerFactoryタイプのcontrollerFactoryパラメータを直接保存しないのか不思議でしたが、GetControllerFactoryの戻り用としてベンドを迂回しなければなりませんでした.factoryThunkという依頼を返しますか?大丈夫です.後を見れば理由がわかりますが、MVC 3.0に拡張性のために加わった新しい処理方法だそうです(誰の話を聞いたのか...).ControllerBuilderがIControllerFactoryを取得する職責をデフォルトのSingleServiceResolverクラスに任せた以上、それがどういうことなのか見てみましょう.
最後の行は、いくつかのサービスの取得方法の優先度を示す戻りコードです.
IDependencyResolverのGetServiceメソッドを優先的に使用した結果.次に、コンストラクション関数を使用して入力されたFuncタイプパラメータの委任を使用して結果を返します.最後に、コンストラクション関数を使用して入力される特定のIControllerFactoryタイプのコントローラファクトリオブジェクトを使用します.ここを見て、少し見覚えがありますか?多くのIoCフレームワークのあのServiceLocatorと似ているようですね?これについては、優先度が明らかになった以上、構造関数のコードに基づいて、この優先度が最も高い依存解析器(IDependencyResolver)がDependencyResolverのCurrent静的属性を指しているので、それを見てみましょう.
ASPを使っているのでNET MVCが開発されたとき、私たちが最初に触れたのはルーティングであり、伝説のSystemでもある.Web.Routing.RouteTable,Reflectionクラスのコードは次のとおりです.
private static RouteCollection _instance = new RouteCollection();
public static RouteCollection Routes
{
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return RouteTable._instance;
}
}
では、単純で簡単なので、RouteCollectionのコードを見てみましょう.このクラスではよく使われていないMapRouteメソッドを発見しました.もともとこのメソッドはMVCフレームワークが拡張されていて、具体的なコードはMVCプロジェクトのソースコードのSystemにあります.Web.Mvc.RouteCollectionExtensionsクラス、次のコードがあります.public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
if (routes == null) {
throw new ArgumentNullException("routes");
}
if (url == null) {
throw new ArgumentNullException("url");
}
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
if ((namespaces != null) && (namespaces.Length > 0)) {
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}
上記のコードは簡単ですが、2つのポイントがあります.MvcRouteHandlerオブジェクトがコンストラクション関数に渡されるRouteオブジェクトが作成されました.新しいRouteオブジェクトのDefault、Constraints、DataTokensの3つのRouteValueDictionaryタイプのプロパティが設定されています.デフォルト値(Defaults)とコンストレイント(Constraints)はobjectのオブジェクトによって初期化されています.RouteValueDictionaryタイプについてはstringをキーobjectとする値の辞書です.具体的なコードはここでは貼らないので、反射してみてください.RouteValueDictionaryというコンストラクション関数とプライベートAddValuesメソッドを見てみましょう.
public RouteValueDictionary(object values)
{
this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
this.AddValues(values);
}
private void AddValues(object values)
{
if (values != null)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(values);
foreach (PropertyDescriptor propertyDescriptor in properties)
{
object value = propertyDescriptor.GetValue(values);
this.Add(propertyDescriptor.Name, value);
}
}
}
上記のAddValueメソッドでは、TypeDescriptorを使用してvaluesパラメータで指定された任意のオブジェクトの共通属性セットを取得し、匿名クラスを使用してRouteオブジェクトの関連属性を設定するのに便利な名前/値で内部辞書に追加します.MapRouteメソッドでは、MvcRouteHandlerのデフォルトのコンストラクション関数を使用してインスタンスを作成します.では、順藤で瓜を触ってみましょう.public class MvcRouteHandler : IRouteHandler {
private IControllerFactory _controllerFactory;
public MvcRouteHandler() {
}
public MvcRouteHandler(IControllerFactory controllerFactory) {
_controllerFactory = controllerFactory;
}
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) {
string controllerName = (string)requestContext.RouteData.Values["controller"];
IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
}
#region IRouteHandler Members
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
return GetHttpHandler(requestContext);
}
#endregion
}
GetHttpHandlerメソッドでは最後に新しいMvcHandlerオブジェクトが返され、このクラスは先にテーブルを押さない.まず、コントローラファクトリの問題を見てみましょう.デフォルトのコンストラクション関数が保持されているためです.ControllerFactoryメンバー変数はnullであるため、GetSessionStateBehaviorメソッドではControllerBuilderによってコントローラファクトリオブジェクトを取得し、どのように取得したかを確認します.public class ControllerBuilder
{
private Func<IControllerFactory> _factoryThunk = () => null;
private static ControllerBuilder _instance = new ControllerBuilder();
private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private IResolver<IControllerFactory> _serviceResolver;
public ControllerBuilder() : this(null)
{
}
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
_serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
() => _factoryThunk(),
new DefaultControllerFactory
{
ControllerBuilder = this
},
"ControllerBuilder.GetControllerFactory"
);
}
public static ControllerBuilder Current
{
get
{
return _instance;
}
}
public HashSet<string> DefaultNamespaces
{
get
{
return _namespaces;
}
}
public IControllerFactory GetControllerFactory()
{
return _serviceResolver.Current;
}
public void SetControllerFactory(IControllerFactory controllerFactory)
{
if(controllerFactory == null)
{
throw new ArgumentNullException("controllerFactory");
}
_factoryThunk = () => controllerFactory;
}
public void SetControllerFactory(Type controllerFactoryType)
{
if(controllerFactoryType == null)
{
throw new ArgumentNullException("controllerFactoryType");
}
if(!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType))
{
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_MissingIControllerFactory,
controllerFactoryType),
"controllerFactoryType");
}
_factoryThunk = delegate()
{
try
{
return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
}
catch(Exception ex)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,
controllerFactoryType),
ex);
}
};
}
}
大まかに見ると、このクラスは主にIControllerFactoryオブジェクトを取得するために使用され、GetControllerFactoryメソッドは簡単で、内部のIresolverinternal class SingleServiceResolver<TService> : IResolver<TService> where TService : class
{
private TService _currentValueFromResolver;
private Func<TService> _currentValueThunk;
private TService _defaultValue;
private Func<IDependencyResolver> _resolverThunk;
private string _callerMethodName;
public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)
{
if(currentValueThunk == null)
throw new ArgumentNullException("currentValueThunk");
if(defaultValue == null)
throw new ArgumentNullException("defaultValue");
_resolverThunk = () => DependencyResolver.Current;
_currentValueThunk = currentValueThunk;
_defaultValue = defaultValue;
_callerMethodName = callerMethodName;
}
public TService Current
{
get
{
if(_resolverThunk != null)
{
lock(_currentValueThunk)
{
if(_resolverThunk != null)
{
_currentValueFromResolver = _resolverThunk().GetService<TService>();
_resolverThunk = null;
if(_currentValueFromResolver != null && _currentValueThunk() != null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.SingleServiceResolver_CannotRegisterTwoInstances, typeof(TService).Name.ToString(), _callerMethodName));
}
}
}
}
return _currentValueFromResolver ?? _currentValueThunk() ?? _defaultValue;
}
}
}
SingleServiceResolverクラスのCurrentプロパティでは、まず判断_resolverThunkメンバー変数が空かどうか、この変数はIDependencyResolverインタフェースを返す依頼であり、コンストラクション関数ではDependencyResolverクラスを指す静的属性Current(この東と東を先に配置)が見られ、次いで標準的な二重ロックの処理であり、具体的なワークコードはこの依頼によって取得されたIDependcencyResolverのGetServiceメソッドによってIControllerFactoryを取得する.取得後すぐにその依頼を空にし、取得したコントローラファクトリオブジェクトが空ではなく、コンストラクション関数を介して伝達された「カーブ回り」の戻りIControllerFactoryの依頼が戻ったコントローラファクトリも空ではないかどうかを判断し、いずれも空でなければ取得方式が競合していることを示し、無効な操作異常を放出し、この異常は2つのサービスインスタンスを取得する方式を登録できないことを示す.最後の行は、いくつかのサービスの取得方法の優先度を示す戻りコードです.
IDependencyResolverのGetServiceメソッドを優先的に使用した結果.次に、コンストラクション関数を使用して入力されたFunc
public class DependencyResolver
{
private static DependencyResolver _instance = new DependencyResolver();
public static IDependencyResolver Current
{
get
{
return _instance.InnerCurrent;
}
}
private IDependencyResolver _current = new DefaultDependencyResolver();
public IDependencyResolver InnerCurrent
{
get
{
return _current;
}
}
public void InnerSetResolver(IDependencyResolver resolver)
{
if(resolver == null)
throw new ArgumentNullException("resolver");
_current = resolver;
}
public void InnerSetResolver(object commonServiceLocator)
{
if(commonServiceLocator == null)
throw new ArgumentNullException("commonServiceLocator");
Type locatorType = commonServiceLocator.GetType();
MethodInfo getInstance = locatorType.GetMethod("GetInstance", new[] { typeof(Type) });
MethodInfo getInstances = locatorType.GetMethod("GetAllInstances", new[] { typeof(Type) });
if(getInstance == null ||
getInstance.ReturnType != typeof(object) ||
getInstances == null ||
getInstances.ReturnType != typeof(IEnumerable<object>))
{
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DependencyResolver_DoesNotImplementICommonServiceLocator,
locatorType.FullName
),
"commonServiceLocator"
);
}
var getService = (Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, getInstance);
var getServices = (Func<Type, IEnumerable<object>>)Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, getInstances);
_current = new DelegateBasedDependencyResolver(getService, getServices);
}
public void InnerSetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
{
if(getService == null)
throw new ArgumentNullException("getService");
if(getServices == null)
throw new ArgumentNullException("getServices");
_current = new DelegateBasedDependencyResolver(getService, getServices);
}
private class DelegateBasedDependencyResolver : IDependencyResolver
{
Func<Type, object> _getService;
Func<Type, IEnumerable<object>> _getServices;
public DelegateBasedDependencyResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
{
_getService = getService;
_getServices = getServices;
}
public object GetService(Type type)
{
try
{
return _getService.Invoke(type);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type type)
{
return _getServices(type);
}
}
private class DefaultDependencyResolver : IDependencyResolver
{
public object GetService(Type serviceType)
{
try
{
return Activator.CreateInstance(serviceType);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
return Enumerable.Empty<object>();
}
}
}