Мы создали приложение ASP.NET MVC 4, которое позволяет динамически загружать «модули» (пакет из одного или нескольких контроллеров, представлений и т. Д.) через MEF. Эти модули расположены по каталогам, под базовым каталогом хоста в папке Modules. Иерархия каталогов выглядит так:ASP.NET MVC4 с MEF: запросы к контроллерам MEF обрабатываются последовательно
/MVC_MEF_Host
/bin
/obj
/Scripts
/Views
/Modules
/Module1
/bin
/Scripts
/Views
/Module2
/bin
/Scripts
/Views
/Module3
...
Однако мы столкнулись с странным поведением. Запросы, которые входят в состав контроллера, который является частью модуля (то есть, находящегося в каталоге/bin/directory) модуля, обрабатываются последовательно, в то время как запросы, сделанные против контроллеров, сборки которых находятся в каталоге хоста/bin /, обрабатываются параллельно (что это то, что мы ожидаем).
Странная часть, я не знаю, как приложение может отличить эти два. Контроллеры в сборке хоста экспортируются так же, как сборки в модулях. Все контроллеры экспортируются с [PartCreationPolicy(CreationPolicy.NonShared)]
. Мы используем фабрику настраиваемых контроллеров для создания экземпляров контроллеров. Единственное различие между двумя типами контроллеров заключается в том, что контроллеры модулей расположены в каталоге/bin/directory модуля, а не в каталоге хоста/bin /.
Существует некоторая специальная обработка, идущая за кулисами. Мы установили несколько тестов, чтобы определить происхождение этой проблемы. Мы обнаружили, что даже если наша фабрика пользовательских контроллеров должна быть первым местом, в котором наше приложение даже рассматривает, какую сборку вытащить из контроллера, запросы, сделанные против контроллеров модулей, поступают последовательно до того, как наша фабрика контроллеров даже вызывается. Другими словами, даже до того, как мы консультировались с контейнером композиции, чтобы определить контроллер для использования, вызовы выполняются последовательно. Когда запросы нацелены на контроллер, чья сборка находится внутри хоста/bin /, вызовы на фабрику контроллера производятся параллельно.
Мы рассмотрели несколько обходных решений (в настоящее время ведущее устройство копирует сборки модулей в хост/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/находятся?
В качестве примечания мы экспортируем наши контроллеры, используя их имя в качестве названия контракта. Наша фабрика пользовательских контроллеров вызывает метод в нашем классе 'PartsBootstrapper' для извлечения контроллера с именем контроллера, который в свою очередь вызывает' partsContainer.GetExportedValue (имя_контакта) ', где' partsContainer' является нашим 'CompositionContainer' и' contractName' является имя контроллера. –
oconnerj
Еще одно замечание для большего контекста: мы обнаружили эту проблему при попытке сделать несколько длительных вызовов AJAX параллельно действиям в контроллере модуля. Мы ожидали, что запросы будут обрабатываться параллельно (независимо от того, какие пакеты были отправлены браузером). Вместо этого мы обнаружили, что, хотя браузер правильно поместил вызовы AJAX и отправил их, наше приложение будет обрабатывать по одному. – oconnerj