如何以及何时填充MVC-ControllerTypeCache.xml

Dav*_*ean 5 c# asp.net-mvc-3

我有几个与此文件相关的问题(MVC-ControllerTypeCache.xml).

1)谁能告诉我这个文件何时以及如何生成?

我知道它是由框架生成的,以减少调用控制器时所需的反射量.

我也知道MVC源中有一些内部类用于处理它,控制器工厂GetControllerType使用它们.

2)有没有办法在应用程序中使用它?

例如,如果我想列出应用程序中的所有控制器,使用此文件意味着我不必通过反射自己找到它们.

还应该知道如何/何时更新它,因为该方法GetControllerType(requestContext, controllerName);将根据它在此文件中找到的内容返回您的控制器类型.

知道何时更新以及是否可以依赖它可能会改变从驻留在自己的程序集中的插件/模块注册控制器的方式.

我主要是纯粹出于兴趣而问.

Dar*_*rov 6

1)谁能告诉我这个文件何时以及如何生成?

DefaultControllerFactory.GetControllerType其上的每个请求而被调用调用 GetControllerTypeWithinNamespaces方法来检索可用的控制器类型的列表:

private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) {
    ControllerTypeCache.EnsureInitialized(BuildManager);
    ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);

    ... more code removed for brevity
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它在开始时做了两件事:初始化和从中检索控制器类型ControllerTypeCache.

EnsureInitialized方法使用具有双重检查锁定的单例来确保在应用程序的整个生命周期内仅执行一次初始化:

public void EnsureInitialized(IBuildManager buildManager) {
    if (_cache == null) {
        lock (_lockObj) {
            if (_cache == null) {
                List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);
                var groupedByName = controllerTypes.GroupBy(
                    t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
                    StringComparer.OrdinalIgnoreCase);
                _cache = groupedByName.ToDictionary(
                    g => g.Key,
                    g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
                    StringComparer.OrdinalIgnoreCase);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意如果_cache字段为null,该字段将仅初始化一次.这将发生在IIS启动应用程序后第一个访问您的站点的请求上.

使用该TypeCacheUtil.GetFilteredTypesFromAssemblies方法检索控制器类型.那么让我们来看看它:

public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) {
    TypeCacheSerializer serializer = new TypeCacheSerializer();

    // first, try reading from the cache on disk
    List<Type> matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer);
    if (matchingTypes != null) {
        return matchingTypes;
    }

    // if reading from the cache failed, enumerate over every assembly looking for a matching type
    matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList();

    // finally, save the cache back to disk
    SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer);

    return matchingTypes;
}
Run Code Online (Sandbox Code Playgroud)

代码非常自我解释:

  1. 它使用一个TypeCacheSerializer类从缓存中读取它们.在内部,此序列化程序将文件加载到a中XmlDocument,然后操纵其元素以提取类型.
  2. 如果没有在缓存中找到任何内容,则对该FilterTypesInAssemblies方法进行昂贵的调用,该方法将使用反射从所有引用的程序集中检索控制器类型.
  3. 它将类型保存到缓存中,以便下次从缓存加载它们.

这是一篇博客文章,其中也描述了这个过程:http://www.beletsky.net/2011/12/inside-aspnet-mvc-instantiation-of.html

2)有没有办法在应用程序中使用它?

您不应该直接使用代码中的XML文件,因为它的内容和格式可能会在将来的版本中发生变化,这会破坏您的代码.

我同意,尽管能够利用我们的代码中的这个功能来提高昂贵的反射代码的性能本来是很好的.我希望框架的作者公开这个API.

不幸的是他们没有,所以我们可以推出自己的:

public static class ControllerTypeCache
{
    private static object _syncRoot = new object();
    private static Type[] _cache;

    public static IEnumerable<Type> GetControllerTypes()
    {
        if (_cache == null)
        {
            lock (_syncRoot)
            {
                if (_cache == null)
                {
                    _cache = GetControllerTypesWithReflection();
                }
            }
        }
        return new ReadOnlyCollection<Type>(_cache);
    }

    private static Type[] GetControllerTypesWithReflection()
    {
        var typesSoFar = Type.EmptyTypes;
        var assemblies = BuildManager.GetReferencedAssemblies();
        foreach (Assembly assembly in assemblies) 
        {
            Type[] typesInAsm;
            try 
            {
                typesInAsm = assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException ex) 
            {
                typesInAsm = ex.Types;
            }
            typesSoFar = typesSoFar.Concat(typesInAsm).ToArray();
        }

        return typesSoFar
            .Where(t => t != null && 
                        t.IsPublic && 
                        !t.IsAbstract && 
                        typeof(IController).IsAssignableFrom(t)
            )
            .ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

还应该知道如何/何时更新方法GetControllerType(requestContext,controllerName); 将根据它在此文件中找到的内容返回您的控制器类型.

在应用程序的整个生命周期内,此文件永远不会更新.如前所述,它在应用程序启动时创建一次.