如何以及何时MVC-ControllerTypeCache.xml得到填充 [英] How and when does MVC-ControllerTypeCache.xml get populated

查看:95
本文介绍了如何以及何时MVC-ControllerTypeCache.xml得到填充的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我知道它由框架生成以减少当控制器调用所需的恢复体力的量

我也知道在MVC源的一些内部类与它的工作,控制器工厂 GetControllerType 利用它们。

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

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

这也将是值得知道如何/时,它作为方法 GetControllerType(RequestContext的,controllerName)被更新; 基于它发现在这将返回您的控制类型文件中。

当它被更新,如果你能依靠它可以改变你的方式注册从插件/驻留在自己组装的模块控制器知道。

我主要问纯粹是出于兴趣,虽然


解决方案

  

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


DefaultControllerFactory.GetControllerType 这是对每个请求调用调用 GetControllerTypeWithinNamespaces 方法来检索可用的控制器列表类型:

 私有类型GetControllerTypeWithinNamespaces(RouteBase路线,串controllerName,HashSet的<串GT;命名空间){
    ControllerTypeCache.EnsureInitialized(BuildManager);
    ICollection的<类型> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName,命名空间);    ...更多code为简洁起见删除
}

正如你可以看到它在开始两件事情:从初始化和控制器类型的检索的 ControllerTypeCache

EnsureInitialized 方法使用双重检查锁定,以确保初始化单为应用程序的整个生命周期进行一次:

 公共无效EnsureInitialized(IBuildManager buildManager){
    如果(_cache == NULL){
        锁定(_lockObj){
            如果(_cache == NULL){
                清单<类型> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName,IsControllerType,buildManager);
                VAR groupedByName = controllerTypes.GroupBy(
                    T => t.Name.Substring(0,t.Name.Length - 控制器。长度)
                    StringComparer.OrdinalIgnoreCase);
                _cache = groupedByName.ToDictionary(
                    G => g.Key,
                    G => g.ToLookup(T => t.Namespace ??的S​​tring.Empty,StringComparer.OrdinalIgnoreCase)
                    StringComparer.OrdinalIgnoreCase);
            }
        }
    }
}

请注意 _cache 字段将只有一次,如果它是空初始化。这将发生在该击中了你的网站上的应用程序已通过IIS启动后的第一个要求。

控制器类型用 TypeCacheUtil.GetFilteredTypesFromAssemblies 方法检索。因此,让我们看看吧:

 公共静态列表<类型> GetFilteredTypesFromAssemblies(字符串cacheName,predicate<类型> predicate,IBuildManager buildManager){
    TypeCacheSerializer串行=新TypeCacheSerializer();    //首先,尝试从磁盘读取缓存
    清单<类型> matchingTypes = ReadTypesFromCache(cacheName,predicate,buildManager,序列化);
    如果(matchingTypes!= NULL){
        返回matchingTypes;
    }    //如果从缓存中读取失败,枚举每个组件寻找匹配的类型
    matchingTypes = FilterTypesInAssemblies(buildManager,predicate).ToList();    //最后,回到保存的缓存到磁盘
    SaveTypesToCache(cacheName,matchingTypes,buildManager,序列化);    返回matchingTypes;
}

在code是pretty自我解释的:


  1. 它使用了 TypeCacheSerializer 类从缓存中读取。该内部串行器文件加载到一个的XmlDocument ,然后操纵它的元素提取类型。

  2. 如果没有被发现,到昂贵的呼叫到将使用反射从所有引用的程序集检索控制器类型 FilterTypesInAssemblies 方法进行缓存。

  3. 它保存类型,以便在下一次将它们从高速缓存加载的高速缓存。

下面是一篇博客文章中也介绍了这个过程:<一href=\"http://www.beletsky.net/2011/12/inside-aspnet-mvc-instantiation-of.html\">http://www.beletsky.net/2011/12/inside-aspnet-mvc-instantiation-of.html


  

2)有没有一种方法在应用程序中使用它的工作?


您不应该直接从您的code这个XML文件的工作,因为它的内容和格式可能会在未来的版本中这将打破你的code修改。

我同意,虽然它本来很高兴能够利用这一功能从我们code,以改善否则昂贵的反射code的性能。祝框架的作者做了这个API公开。

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

 公共静态类ControllerTypeCache
{
    私人静态对象_syncRoot =新的对象();
    私有静态类型[_cache]    公共静态的IEnumerable&LT;类型&GT; GetControllerTypes()
    {
        如果(_cache == NULL)
        {
            锁定(_syncRoot)
            {
                如果(_cache == NULL)
                {
                    _cache = GetControllerTypesWithReflection();
                }
            }
        }
        返回新ReadOnlyCollection还&LT;类型和GT;(_缓存);
    }    私有静态类型[] GetControllerTypesWithReflection()
    {
        VAR typesSoFar = Type.EmptyTypes;
        无功组件= BuildManager.GetReferencedAssemblies();
        的foreach(在组件装配装配)
        {
            键入[] typesInAsm;
            尝试
            {
                typesInAsm = assembly.GetTypes();
            }
            赶上(ReflectionTypeLoadException前)
            {
                typesInAsm = ex.Types;
            }
            typesSoFar = typesSoFar.Concat(typesInAsm).ToArray();
        }        返回typesSoFar
            。凡(T =&GT;!T =空&放大器;&安培;
                        t.IsPublic&功放;&安培;
                        !t.IsAbstract&放大器;&安培;
                        typeof运算(一个IController).IsAssignableFrom(T)
            )
            .ToArray();
    }
}


  

这也将是值得知道如何/时,作为被更新
  方法GetControllerType(RequestContext的,controllerName);将返回
  根据其发现在这个文件中的控制器类型。


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

I have a couple of questions related to this file (MVC-ControllerTypeCache.xml).

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

I know it is generated by the framework to reduce the amount of refection needed when controllers are called.

I also know there are a few internal class in MVC source for working with it, the controller factory GetControllerType makes use of them.

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

For example if I want to list all the controllers in the application, using this file would mean I do not have to find them all myself via reflection.

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.

Knowing when it gets updated and if you can rely on it could change the way you register controllers from plugins / modules that reside in their own assemblies.

I mainly ask purely out of interest though.

解决方案

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

The DefaultControllerFactory.GetControllerType which is invoked on each request calls the GetControllerTypeWithinNamespaces method to retrieve the list of available controller types:

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
}

As you can see it does 2 things in the beginning: initialization and retrieving of controller types from the ControllerTypeCache.

The EnsureInitialized method uses a singleton with double checked locking to ensure that initialization is performed only once for the entire lifetime of the application:

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

Notice how the _cache field will be initialized only once if it is null. This will happen on the very first request that hits your site after the application has started by IIS.

The controller types are retrieved using the TypeCacheUtil.GetFilteredTypesFromAssemblies method. So let's look into it:

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

The code is pretty self explanatory:

  1. it uses a TypeCacheSerializer class to read them from the cache. Internally this serializer loads the file into a XmlDocument and then manipulates its elements to extract the types.
  2. if nothing is found into the cache an expensive call is made to the FilterTypesInAssemblies method that will use reflection to retrieve the controller types from all referenced assemblies.
  3. it saves the types to the cache so that next time they are loaded from the cache.

Here's a blog post which also describes the process: http://www.beletsky.net/2011/12/inside-aspnet-mvc-instantiation-of.html

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

You are not supposed to work directly with this XML file from your code because its contents and format could change in future versions which would break your code.

I agree though that it would have been nice to be able to take advantage of this feature from our code to improve the performance of otherwise expensive reflection code. I wish the authors of the framework had made this API public.

Unfortunately they haven't, so we could roll our own:

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.

This file never gets updated during the whole lifetime of the application. As explained earlier it is created once, when the application starts.

这篇关于如何以及何时MVC-ControllerTypeCache.xml得到填充的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆