c# - 带有 MEF 的 ASP.NET MVC4 : Requests to MEF Controllers Processed Serially

标签 c# asp.net-mvc-4 plugins mef

我们创建了一个 ASP.NET MVC 4 应用程序,它允许通过 MEF 动态加载“模块”(一个或多个 Controller 、 View 等的包)。这些模块按目录位于主机基本目录下名为 Modules 的文件夹中。目录层次结构如下所示:

/MVC_MEF_Host
    /bin
    /obj
    /Scripts
    /Views
    /Modules
        /Module1
            /bin
            /Scripts
            /Views
        /Module2
            /bin
            /Scripts
            /Views
        /Module3
            ...

但是,我们遇到了奇怪的行为。针对属于模块一部分的 Controller (即驻留在模块的/bin/目录中)的请求将按顺序处理,而针对其程序集驻留在主机应用程序的/bin/目录中的 Controller 发出的请求将被并行处理(其中是我们所期望的)。

奇怪的是,我不确定应用程序如何区分两者之间的区别。主机程序集中的 Controller 就像模块中的程序集一样导出。所有 Controller 均通过[PartCreationPolicy(CreationPolicy.NonShared)]导出。我们使用自定义 Controller 工厂来实例化 Controller 。两种类型的 Controller 之间的唯一区别是模块 Controller 位于模块的/bin/目录中,而不是主机的/bin/目录中。

幕后正在进行某种特殊处理。我们设置了多项测试来确定此问题的根源。我们发现,即使我们的自定义 Controller 工厂应该是我们的应用程序考虑从哪个程序集提取 Controller 的第一个地方,针对模块 Controller 发出的请求甚至在我们的 Controller 工厂被调用之前就按顺序出现。换句话说,即使在我们查阅组合容器以确定要使用的 Controller 之前,调用也是按顺序进行的。当请求的目标 Controller 的程序集驻留在主机/bin/中时,对 Controller 工厂的调用是并行进行的。

我们考虑了几种解决方法(当前领先的一种是将模块程序集复制到主机的/bin/中),但每种解决方法都有一个严重的缺点,会影响我们预期的工作流程。

我已经包含了执行合成的方法的精简版本,以防这是我们在合成过程中所做的事情的副作用。

static void _Compose(List<string> moduleDirectories)
{
    ResetViewEngine(moduleDirectories);         // adds the modules' /Views/ paths to the view engine

    AggregateCatalog aggCat = new AggregateCatalog();

    // Load host bin directory
    aggCat.Catalogs.Add(new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")));

    foreach (string pluginPath in moduleDirectories)
    {
        string modulePath = Path.Combine(pluginPath, "bin");

        if (Directory.Exists(modulePath))
        {
            // Add directory to private paths, required for plugin dependencies                                   
            #pragma warning disable 618
            AppDomain.CurrentDomain.AppendPrivatePath(modulePath);  // this is obsolete, but since we're not constructing an appDomain...
            #pragma warning restore 618

            foreach (string file in Directory.GetFiles(modulePath, PLUGIN_FILENAME_PATTERN))
            {
                Assembly asm = LoadAssemblyFromFile(file);      // reads the bytes of the assembly into memory and then loads via Assembly.Load()

                if (asm != null)
                {
                    AssemblyCatalog ac = new AssemblyCatalog(asm);
                    aggCat.Catalogs.Add(ac);
                }
                else
                {
                    Log("Could not load assembly: " + file);
                }
            }
        }
        else
        {
            Log("Module path " + modulePath + " does not exist.");
        }
    }

    // partsContainer is a private static member of our PartsBootstrapper class
    partsContainer = new CompositionContainer(aggCat, true);
    partsContainer.ComposeParts();
}

那么,如何才能像主机/bin/中的 Controller 请求一样并行处理模块 Controller 的请求呢?

最佳答案

对于将来遇到此问题的任何人,我们找到了解决方案。我不一定明白它为什么有效,但它似乎确实有效。

本质上,我们的自定义 Controller 工厂继承自 DefaultControllerFactory 。我们依赖 GetControllerSessionBehavior 的默认实现方法,事实证明它始终是 SessionStateBehavior.Default。通过更改我们的 Controller 工厂来继承 IControllerFactory ,我们能够定义我们自己的 GetControllerSessionBehavior 的实现。

我们得到了实现返回 SessionStateBehavior.ReadOnly但这还不够。 MVC 根本阻止我们写入 session ,尽管我们已经读到它实际上允许 session 状态行为为只读时进行 session 写入(并且只是回退到 SessionStateBehavior.Required 状态)。因此,我们添加了一个新的 ExportMetadata这允许我们指定 SessionStateBehavior对于GetControllerSessionBehavior方法,这似乎有效。现在,我们默认为 SessionStateBehavior.ReadOnly除非元数据指定了不同的行为。

我们仍然不太明白为什么主机的/bin/中的程序集与模块程序集的处理方式不同,但这个解决方案对我们有用。

关于c# - 带有 MEF 的 ASP.NET MVC4 : Requests to MEF Controllers Processed Serially,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28155024/

相关文章:

c# - 难倒自托管 Web Api 服务

ruby-on-rails - 捆绑安装错误 : Source does not contain any versions of

python - 如何从 `sublime console` 运行 WindowCommand 插件

c# - 每分钟从 API 获取数据到我的服务器

c# - 我可以为 dapper-dot-net 映射指定数据库列名吗?

jquery - 将 List<int> 从模型传递到 jQuery int[]

plugins - Grails Maven依赖解析

c# - WPF 自定义控件(多选组合框)的问题

c# - 如何序列化嵌套的 JSON?

javascript - 将大型应用程序转换为 SPA