Свойство MEF всегда возвращает значение Null

#c# #import #export #properties #mef

#c# #импорт #экспорт #свойства #mef

Вопрос:

Оригинальный Исходный код

У меня есть простой бизнес-объект в моих BusinessObjects.dll-файл:

 namespace BusinessObjects
{
    public class MyClass
    {
        public MyClass()
        {
            DateTime = DateTime.Now;
        }

        public DateTime DateTime { get; set; }
    }
}
  

В моем SharedUI.dll У меня есть этот класс «Context-provider», который я использую для хранения ссылки на выбранный в данный момент MyClass — помните, что это упрощенный пример :)…

 namespace SharedUI
{
    public class AppContext
    {
        [Export]
        public MyClass SelectedMyClass { get; private set; }

        public void SetupContext(MyClass myClass)
        {
            SelectedMyClass = myClass;
        }

        public static AppContext Context
        {
            get
            {
                if (context == null)
                {
                    context = new AppContext();
                }
                return context;
            }
        }

        private static AppContext context;
    }
}
  

Мой MefTest.exe имеет этот класс:

 namespace MefTest
{
    public class Program
    {
        [Import]
        public MyClass MyClass { get; set; }

        private void Compose()
        {
            var ventSystem = new MyClass();
            AppContext.Context.SetupContext(ventSystem);

            var executingAssembly = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var contextAssembly = new AssemblyCatalog(Assembly.LoadFile(string.Format(@"{0}SharedUI.dll", Environment.CurrentDirectory)));
            var catalog = new AggregateCatalog(executingAssembly, contextAssembly);

            var container = new CompositionContainer(catalog);

            container.ComposeParts(this);
        }

        private void Run()
        {
            Compose();

            // MyClass is always null in the next line?
            Console.WriteLine(MyClass.DateTime.ToString());

            Console.ReadKey();
        }

        private static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }
    }
}
  

Я новичок в MEF, поэтому, пожалуйста, потерпите меня 🙂

ОБНОВЛЕННЫЙ исходный код с предложениями от Дэниела Плейстеда

Источник MyClass тот же…

SharedUI.dll теперь выглядит так:

 namespace SharedUI
{
    [Export]
    public class AppContext
    {
        [Export(typeof(MyClass))]
        public MyClass SelectedMyClass { get; private set; }

        public void SetupContext(MyClass myClass)
        {
            SelectedMyClass = myClass;
        }
    }
}
  

MefTest.exe теперь выглядит так:

 namespace MefTest
{
    public class Program
    {
        [Import]
        public MyClass MyClass { get; set; }

        [Import]
        public AppContext AppContext { get; set; }

        private void Compose()
        {
            var executingAssembly = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var contextAssembly = new AssemblyCatalog(Assembly.LoadFile(string.Format(@"{0}SharedUI.dll", Environment.CurrentDirectory)));
            var catalog = new AggregateCatalog(executingAssembly, contextAssembly);

            var container = new CompositionContainer(catalog);

            container.ComposeParts(this);

            var myClass = new MyClass();
            AppContext.SetupContext(myClass);
        }

        private void Run()
        {
            Compose();

            // AppContext.SelectedMyClass is NOT null in the next line... which is good I guess :)
            Console.WriteLine(AppContext.SelectedMyClass.DateTime.ToString());

            // MyClass is always null in the next line?
            Console.WriteLine(MyClass.DateTime.ToString());

            Console.ReadKey();
        }

        private static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }
    }
}
  

Что я делаю не так, поскольку не могу заставить его работать?

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

1. Вы проверили, что оно загружает GUI в каталог AssemblyCatalog?

2. @Jonas: Что вы подразумеваете под «графическим интерфейсом»? Это проект, содержащий Program-class, AppContext-class или MyClass-class …?

3. Извините, я путаю 2 разных вопроса SO 🙂 Я имею в виду класс AppContext. Вы подтвердили, что оно загружается MEF?

4. @Jonas: Я так думаю. Если я удалю Export-attribute из класса AppContext, контейнер. ComposeParts(this) выдает исключение: «Не найдено допустимых экспортов, соответствующих ограничению ‘((exportDefinition. ContractName == «BusinessObjects. MyClass»)…»

Ответ №1:

Когда MEF необходимо получить экспорт, который находится в свойстве класса, он создаст экземпляр класса и вызовет средство получения свойства. Итак, MEF создает новый экземпляр вашего AppContext, отличный от статического экземпляра AppContext.Context. Для экземпляра, созданного MEF, не установлено свойство SelectedMyClass, поэтому ваш импорт заканчивается нулевым значением.

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

1. Я обновил исходный вопрос вашими предложениями — однако я не могу заставить его работать? Чего я здесь не понимаю?

2. @kenneth В вашем измененном примере другая проблема. MEF получает значение для экспорта только один раз и не знает, изменится ли оно позже. Когда вы вызываете ComposeParts(this), оно извлекает значение для экспорта MyClass. Когда вы впоследствии изменяете это свойство, это никак не влияет на значение экспорта.

Ответ №2:

Проблема в том:

     [Import]        public MyClass MyClass { get; set; }
  

Для MyClass не определены [Export] ы. MEF создаст это приложение на основе того, что он «знает», и поскольку он не знает «MyClass»…

Я заметил это:

     [Export]        public MyClass SelectedMyClass { get; private set; }
  

Это означает, что вы пытаетесь обмануть MEF, время от времени обновляя одну из его частей? Решением этой проблемы было бы создать пользовательский каталог, содержащий объекты «runtime», в котором вы можете обновлять экспортированное значение для MyClass, когда захотите. Текущая реализация никогда не разрешит MyClass …

[отредактировано:] Вы также можете оформить элемент, но вам придется добавить туда тип класса. Так что это сработает:

[Export(typeof(MyClass)] public MyClass SelectedMyClass { получить; частный набор; }

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

1. К сожалению, это, похоже, не исправляет проблему: (

Ответ №3:

Вы поместили свой Export атрибут не в то место.

Вы должны поместить его в определение MyClass вот так:

 namespace BusinessObjects
{
[Export]
public class MyClass
{
    public MyClass()
    {
        DateTime = DateTime.Now;
    }

    public DateTime DateTime { get; set; }
}
}
  

А затем используйте атрибут [Import] везде, где вам нужен экземпляр этого класса.

Замечание: Вы не можете использовать MEF для перемещения определенного экземпляра класса (не так, как это). MEF используется для создания экземпляров запрошенного типа и внедрения их в указанные места.

Чтобы узнать больше о MEF, загляните на страницу проекта в CodePlex.

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

1. Если я правильно читаю ссылку на документацию, атрибут Export может использоваться для классов, методов, свойств и полей… Особенно меня интересует следующая цитата в разделе » Примечания»: «Атрибут ExportAttribute может украшать либо весь класс, либо свойство, поле или метод класса. Если оформлен весь класс, экспортируемым объектом является экземпляр класса. Если член класса оформлен, экспортируемый объект будет значением оформленного члена. »

2. Попробуйте [Export(typeof(MyClass)] public MyClass SelectedMyClass { получить; частный набор; }

3. @maartenba: К сожалению, это, похоже, не исправляет проблему : (