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

标签 c# asp.net-mvc-3

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

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

我知道它是由框架生成的,以减少调用 Controller 时所需的反射量。

我也知道在 MVC 源代码中有一些内部类可以使用它, Controller 工厂 GetControllerType 使用它们。

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

例如,如果我想列出应用程序中的所有 Controller ,使用这个文件意味着我不必通过反射自己找到它们。

还值得了解它如何/何时更新,因为方法 GetControllerType(requestContext, controllerName); 将根据它在此文件中找到的内容返回您的 Controller 类型。

了解它何时更新以及您是否可以依赖它可能会改变您从驻留在它们自己的程序集中的插件/模块注册 Controller 的方式。

虽然我主要是出于兴趣而问。

最佳答案

1) Can anyone tell me when and how this file is generated?

在每个请求上调用的 DefaultControllerFactory.GetControllerType 调用 GetControllerTypeWithinNamespaces 方法来检索可用 Controller 类型的列表:

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
}

如您所见,它在开始时做了两件事:从 ControllerTypeCache 初始化和检索 Controller 类型。

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

注意 _cache 字段在为 null 时如何只初始化一次。这将发生在 IIS 启动应用程序后到达您的站点的第一个请求。

使用 TypeCacheUtil.GetFilteredTypesFromAssemblies 方法检索 Controller 类型。那么让我们来研究一下:

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;
}

代码很容易解释:

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

这里有一篇博文也描述了这个过程:http://www.beletsky.net/2011/12/inside-aspnet-mvc-instantiation-of.html

2) Is there a way to work with it in an application ?

您不应该在您的代码中直接使用此 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();
    }
}

It would also be worth knowing how / when it gets updated as the method GetControllerType(requestContext, controllerName); will return your controller type based on what it finds in this file.

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

关于c# - MVC-ControllerTypeCache.xml 如何以及何时填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12056777/

相关文章:

c# - ASP.NET MVC 中 TextBoxFor 的默认值

c# - DDD : How should adding and removing related entities be modelled?

c# - NHibernate:从多对多关系中删除 child 会导致异常

c# - 在保持 "Combine files..."书签结构的同时以编程方式合并 PDF?

c# - ajax 中的 get 请求到 Controller 操作不起作用?

javascript - 使用 jquery 加载和插入 ASP.NET 分部 View

asp.net-mvc-3 - 是否有一种自动方法可以在 MVC 中查找未使用的 View ?

asp.net-mvc-3 - 动态加载部分 View

c# - 在 C# 中映射网络驱动器路径

c# - 如何使 EF Core 工具从控制台应用程序的服务提供者处获取 DbContext 实例?