Ошибка MVC MEF: убедитесь, что контроллер имеет общедоступный конструктор без параметров

#c# #model-view-controller #mef

#c# #модель-представление-контроллер #mef

Вопрос:

Для приложения MVC, использующего MEF, иногда я получаю ошибку «»

У меня есть решение .NET с

  1. Один проект веб-приложения MVC и,
  2. Многие проекты библиотек классов, которые заботятся об аутентификации, получении конфигураций, выполнении вызовов внешнего API и т.д.

Я настроил MEF, как показано ниже, с помощью кода и развернул его на веб-сервере с использованием IIS. Пару раз наблюдал приведенную ниже ошибку, после чего я несколько раз пытался загрузить страницу, но она по-прежнему выдает ту же ошибку.

Как только я обновил пул приложений, исчезает только ошибка. Я изо всех сил пытался отладить и понять ошибку, но безуспешно. Я где-нибудь неправильно настраиваю MEF?

введите описание изображения здесь

Global.asax:

 public class Global : HttpApplication
  {
    void Application_Start(object sender, EventArgs e)
    {
      AreaRegistration.RegisterAllAreas();
      GlobalConfiguration.Configure(WebApiConfig.Register);
      RouteConfig.RegisterRoutes(RouteTable.Routes);

      var pluginFolders = LoadMefComponents();
      Bootstrapper.Compose(pluginFolders);
      IControllerFactory mefControllerFactory = new MefControllerFactory(Bootstrapper.Container);
      ControllerBuilder.Current.SetControllerFactory(mefControllerFactory);
    }

    protected List<string> LoadMefComponents()
    {
      var pluginFolders = new List<string>();
      string ModulesPath = CommonUtility.GetApplicationDirectory();
      var plugins = Directory.GetDirectories(ModulesPath).ToList();
      plugins.ForEach(path =>
      {
        var directoryInfo = new DirectoryInfo(path);
        pluginFolders.Add(directoryInfo.Name);
      });
      return pluginFolders;
    }
  }
  

MEFControllerFactory.cs: Этот файл находится в App_Start

 public class MefControllerFactory : DefaultControllerFactory
    {
        private readonly CompositionContainer _container;
        private readonly Dictionary<IController, Lazy<object, object>> exports;
        private readonly object syncRoot;

        public MefControllerFactory(CompositionContainer container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }

            this._container = container;
            this.exports = new Dictionary<IController, Lazy<object, object>>();
            this.syncRoot = new object();

        }

        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            Lazy<object, object> export = _container.GetExports(controllerType, null, null).FirstOrDefault();

            var controller = null == export ? base.GetControllerInstance(requestContext, controllerType)
                                : (IController)export.Value;
            lock (this.syncRoot)
            {
                this.exports.Add(controller, export);
            }
            return controller;
        }

        public override void ReleaseController(IController controller)
        {
            lock (this.syncRoot)
            {
                var export = this.exports[controller];
                this.exports.Remove(controller);
               // this._container.ReleaseExport(export);
            }
            ((IDisposable)controller).Dispose();
        }
    }

  

Bootstrapper.cs: Этот файл находится в App_Start

 public class Bootstrapper
    {
        private static CompositionContainer compositionContainer;
        private static bool IsLoaded = false;

        public static CompositionContainer Container
        {
            get { return compositionContainer; }
            set { compositionContainer = value; }
        }

        public static void Compose(List<string> pluginFolders)
        {
            if (IsLoaded) return;
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            catalog.Catalogs.Add(new DirectoryCatalog(CommonUtility.GetApplicationDirectory()));
            compositionContainer = new CompositionContainer(catalog);
            compositionContainer.ComposeParts();
            IsLoaded = true;
        }

        public static T GetInstance<T>(string contractName = null)
        {
            var type = default(T);
            if (compositionContainer == null) return type;
            if (!string.IsNullOrWhiteSpace(contractName))
                type = compositionContainer.GetExportedValue<T>(contractName);
            else
                type = compositionContainer.GetExportedValue<T>();
            return type;
        }
    }
  

CommonUtility.cs: This file resides in App_Start

 public class CommonUtility
    {

        public static string GetApplicationDirectory()
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            UriBuilder uri = new UriBuilder(codeBase);
            string path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path);

        }
    }
  

HomeController.cs:

 [CommonExceptionFilter]
  public class HomeController : Controller
  {
    private IConfigurationManager _configurationManager;

    [ImportingConstructor]
    public HomeController()
    {
      _configurationManager = Bootstrapper.GetInstance<IConfigurationManager>();
    }

    public async Task<ActionResult> Index()
    {
      //Business Logic
      return View()

    }
  }

  

IConfigurationManager.cs

 [InheritedExport]
    public interface IConfigurationManager
    {
        string GetConfigurationValue(string keyName)
    }
  

ConfigurationManager.cs

 [PartCreationPolicy(CreationPolicy.Shared)]
    public class ConfigurationManager: IConfigurationManager
    {
        [ImportingConstructor]
        public ConfigurationManager()
        {

        }

        public string GetConfigurationValue(string keyName)
        {
            return "";
        }
    }
  

IHttpHandlers, используемые в проекте MVC:

 public class CommonServiceHandler : HttpTaskAsyncHandler, IRequiresSessionState
  {
    private ICommonServiceHandlerManager _commonServiceHandlerManager;


    public CommonServiceHandler()
    {
      _commonServiceHandlerManager = Bootstrapper.GetInstance<ICommonServiceHandlerManager>();
    }


    public override bool IsReusable
    {
      get
      {
        return false;
      }
    }

  }
  

Ответ №1:

Эта ошибка указывает на то, что существует по крайней мере один контроллер, параметры конструктора которого не разрешены.
Каждому контроллеру нужен конструктор, который должен быть разрешен во время выполнения. По умолчанию у каждого класса c # есть конструктор по умолчанию (без параметров), который может быть вызван, когда необходимо создать экземпляр класса.
Однако после определения явного конструктора вы теряете конструктор по умолчанию, поэтому вам нужно убедиться, что все ваши контроллеры имеют либо конструктор без параметров, либо, если вместо этого у них есть параметрический конструктор, параметры необходимо зарегистрировать посредством внедрения зависимостей.

Комментарии:

1. Я проверил, но у всех контроллеров определен один параметрический конструктор. Внутри приложения MVC есть один контроллер WebAPI, у которого нет параметрического конструктора. Может ли это вызвать проблему?? Эта проблема возникает очень спорадически, не очень часто. Если это из-за контроллера WebAPI, то разве это не должно происходить всегда. Кроме того, я только что проверил, что не добавил [Export] [PartCreationPolicy(CreationPolicy.NonShared)] выше класс контроллера. Может ли это вызвать проблему?

2. Нет, это происходит не последовательно, это происходит только при попытке вызвать метод с этого конкретного контроллера. Вы можете найти контроллер, проверив, какие методы вызывают эту проблему.

3. Я проверил еще раз, но у всех контроллеров есть параметрический конструктор. Не уверен, что происходит не так. При показанной реализации требуется ли добавлять [Export] [PartCreationPolicy(Политика создания. Не разделяемый)] выше класса контроллера?? Я вызываю Bootstrapper. Получите INSTANCE в каждом из параметрических конструкторов.

4. Все ли параметры во всех конструкторах зарегистрированы (введены)?

5. Насколько я знаю, export атрибут не требуется.