MEF и ASP.NET MVC

Я хочу использовать MEF с asp.net mvc. Я записал следующую фабрику контроллера:

public class MefControllerFactory : DefaultControllerFactory
{
    private CompositionContainer _Container;

    public MefControllerFactory(Assembly assembly)
    {
        _Container = new CompositionContainer(new AssemblyCatalog(assembly));
    }



    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType != null)
        {
            var controllers = _Container.GetExports<IController>();

            var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault();

            if (controllerExport == null)
            {
                return base.GetControllerInstance(requestContext, controllerType);
            }

            return controllerExport.Value;
        }
        else
        {
            throw new HttpException((Int32)HttpStatusCode.NotFound,
                String.Format(
                    "The controller for path '{0}' could not be found or it does not implement IController.",
                    requestContext.HttpContext.Request.Path
                )
            );
        }
    }
}

В Global.asax.cs я устанавливаю свою фабрику контроллера:

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterRoutes(RouteTable.Routes);

        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory.MefControllerFactory(Assembly.GetExecutingAssembly()));
    }

У меня есть область:

[Export(typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
    private readonly IArticleService _articleService;

    [ImportingConstructor]
    public HomeController(IArticleService articleService)
    {
        _articleService = articleService;
    }

    //
    // GET: /Articles/Home/

    public ActionResult Index()
    {
        Article article = _articleService.GetById(55);

        return View(article);
    }

}

IArticleService интерфейс.

Существует класс, который реализует IArticleService и Экспорт это.

Работает.

Это - все, в чем я нуждаюсь для работы с MEF?

Как я могу пропустить установку PartCreationPolicy и ImportingConstructor для контроллера?

Я хочу установить свои зависимости с помощью конструктора.

Когда PartCreationPolicy отсутствует, я получаю следующее исключение:

Единственный экземпляр контроллера 'MvcApplication4. Области. Статьи. Контроллеры. HomeController' не может использоваться для обрабатывания нескольких запросов. Если пользовательская фабрика контроллера используется, удостоверьтесь, что она создает новый экземпляр контроллера для каждого запроса.

7
задан DaveRandom 25 February 2013 в 23:09
поделиться

4 ответа

Я решил вернуться в Unity.

Я создал настраиваемый атрибут вместо ExportAttribute MEF

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ImplementsAttribute : Attribute
{
    public ImplementsAttribute(Type contractType)
    {
        ContractType = contractType;
    }

    public Type ContractType
    {
        get; 
        private set;
    }
}

Пример:

[Implements(typeof(ICustomerEmailService))]
public class CustomerEmailService : ICustomerEmailService
{...}

И настраиваемую фабрику контроллеров:

public class MyControllerFactory : DefaultControllerFactory
{
    private readonly IUnityContainer _container;

    public MyControllerFactory()
    {
        _container = new UnityContainer();

        Func<Type, bool> isController =
            (type) => typeof(IController).IsAssignableFrom(type)
                    && (type.IsAbstract || type.IsInterface 
                        || type.GetCustomAttributes(typeof(GeneratedCodeAttribute), true).Any()) != true;



        foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
        {
            try
            {
                var types = assembly.GetTypes();

                // Also register all controllers
                var controllerTypes = from t in types where isController(t) 
                                        select t;


                foreach (Type t in controllerTypes)
                {
                    _container.RegisterType(t);
                }


                // register all providers
                var providers =
                    from t in types
                    from export in t.GetCustomAttributes(typeof(ImplementsAttribute), true).OfType<ImplementsAttribute>()
                    select new { export.ContractType, Provider = t };

                foreach (var item in providers)
                {
                    if (item.ContractType != null)
                    {
                        _container.RegisterType(item.ContractType, item.Provider);
                    }
                    else
                    {
                        _container.RegisterType(item.Provider);
                    }
                }
            }
            catch 
            {
            }
        }
    }



    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType != null)
        {
            var controller = _container.Resolve(controllerType) as IController;

            if (controller == null)
            {
                return base.GetControllerInstance(requestContext, controllerType);
            }

            return controller;
        }


        throw new HttpException((Int32)HttpStatusCode.NotFound,
            String.Format(
                "The controller for path '{0}' could not be found or it does not implement IController.",
                requestContext.HttpContext.Request.Path)
        );
    }
}

Мне было слишком сложно исправить все проблемы для фабрики контроллеров MEF: (

2
ответ дан 7 December 2019 в 09:56
поделиться

В последнее время я много работаю с MEF/MVC и пишу в блоге о своем пересмотренном дизайне MEF+MVC. Я надеюсь скоро выложить его на CodePlex, а пока посмотрите, не поможет ли вам что-нибудь из этого:

  1. Modular ASP.NET MVC using the Managed Extensibility Framework (MEF), Part One
  2. Modular ASP.NET MVC using the Managed Extensibility Framework (MEF), Part Two
0
ответ дан 7 December 2019 в 09:56
поделиться

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

Если вы работаете с полным доверием и хотите, чтобы обнаружение на основе соглашений с вашими контроллерами началось с примера MEF Nerd Dinner, вам, вероятно, также следует прочитать о нескольких основных проблемах с примером MEF с ужином для ботаников, которые возникнут, если вы Модель собственного приложения находится в отдельном классе проекта библиотеки. Я писал об этих случаях и предлагал некоторые исправления.

Если вас не слишком интересуют открытия, основанные на соглашениях, то образец ужина для ботаников будет немного перестроен. Ваше решение, вероятно, в порядке ... и работает с частичным доверием, что всегда является бонусом.

[обновление] Я обнаружил одну потенциальную проблему с вашей техникой:

var controllerExport = controllers.Where(x => x.Value.GetType() == 
controllerType).FirstOrDefault();

В предложении where здесь вы вызываете .Value для каждого экспорта в коллекции частей ... что фактически заставит каждый из этих экспортов быть скомпонован и создан для оценки. Это могло быть неприятной проблемой с производительностью.

Вы можете подумать об украшении ваших контроллеров именованными экспортными контрактами следующим образом:

[Export("Home", typeof(IController))]

Затем вместо этого используйте фабрику контроллеров, подобную этой:

public class MefControllerFactory: IControllerFactory
{
    private CompositionContainer _Container;

    public MefControllerFactory(Assembly assembly)
    {
        _Container = new CompositionContainer(new AssemblyCatalog(assembly));
    }

    #region IControllerFactory Members

    public IController CreateController(RequestContext requestContext, string controllerName)
    {

        var controller = _Container.GetExportedValue<IController>(controllerName);

        if (controller == null)
        {
            throw new HttpException(404, "Not found");
        }

        return controller;
    }

    public void ReleaseController(IController controller)
    {
       // nothing to do
    }

    #endregion
}
4
ответ дан 7 December 2019 в 09:56
поделиться

Спасибо.

Я обнаружил одну потенциальную проблему с вашей техникой:

var controllerExport = controllers.Where (x => x.Value.GetType () == controllerType) .FirstOrDefault ();

Да, это правда.

Прочитав это ( http://codepaste.net/yadusn ), я понял, как был реализован NerdDinner с MEF.

Я использовал стандартный каталог для MEF и создал свою фабрику контроллеров MEFed (без атрибута Export на контроллерах).

 public static IController GetController(CompositionContainer container, Type controllerType)
    {
        var controllers = container.GetExports<IController, IDictionary<string, object>>();

        if (controllers == null) return null;

        var controllerExport = controllers
            .Where(exp => ExportMetadataContainsGuid(exp.Metadata, controllerType.GUID))
            .FirstOrDefault();

        return (controllerExport == null) ? null : controllerExport.Value;
    }

Метод ExportMetadataContainsGuid:

public static bool ExportMetadataContainsGuid(IDictionary<string, object> metaData, Guid guid)
    {
        return metaData.ContainsKey(MetadataGuidKey) && guid == (Guid)metaData[MetadataGuidKey];
    }

Я использую метаданные для хранения GUID типа и использую их для поиска нужного контроллера.

0
ответ дан 7 December 2019 в 09:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: