Новая ошибка MEF, которую я не видел раньше - «Экспорт не может быть назначен для типа ...» - PullRequest
2 голосов
/ 23 декабря 2010

Я был очень удивлен, получив эту ошибку сегодня, поскольку с ней я никогда не сталкивался раньше. Все в коде выглядело нормально, поэтому я провел несколько поисков. Предыдущие вопросы и соответствующие ответы не помогли.


Этот был решен, когда автор убедился, что ссылки на его сборку были последовательными У меня сейчас нет этой проблемы, потому что я сейчас ссылаюсь на другой проект в своем решении.

Этот был решен, когда постеру было приказано использовать ImportMany, но я уже использую его (я тоже думаю, что правильно), чтобы попытаться загрузить несколько плагинов

Это было решено, когда автор понял, что было несоответствие цели платформы. Я уже просмотрел свои проекты, чтобы убедиться, что все нацелено на x86.


Так вот что я пытаюсь сделать. У меня есть плагин, который владеет подключением к устройству. Мне также может понадобиться поделиться этим подключением с другим плагином. Я решил, что самый простой способ сделать это - создать интерфейс, который позволял бы подключаемому плагину запрашивать собственное соединение с устройством. Давайте просто назовем это IConnectionSharer. Если подключаемому плагину не необходимо заимствовать это соединение и он имеет свое собственное, то для подключения к устройству он должен использовать собственную реализацию IConnectionSharer.

Мой плагин "master" (тот, который владеет подключением к устройству) реализует IConnectionSharer. Это также экспортирует это через ExportAttribute.

Моя сборка плагина "slave" определяет класс, который также реализует и экспортирует IConnectionSharer.

Когда приложение загружается, цель заключается в том, чтобы мой плагин slave через MEF перечислил все IConnectionSharer s и сохранил их в IEnumerable<IConnectionSharer>. Это так:

[ImportMany]
public IEnumerable<IConnectionSharer> AllSharedConnections { get; set; }

Николай попросил меня показать мои Export с, так что вот они.

Плагин "Мастер":

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(Interface1))]
[Export(typeof(Interface2))]
[Export(typeof(Interface3))]
[Export(typeof(IConnectionSharer))]
public partial class MasterPlugin : Interface1, Interface2, Interface3, IConnectionSharer
{
    ...
}

Плагин "Slave":

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(Interface1))]
public class SlavePlugin : Interface1
{
    private Model _model { get; set; }
    private ViewModel _viewmodel { get; set; }

    [ImportingConstructor]
    public SlavePlugin( [Import] Model model) 
    {
        _model = model;
        _viewmodel = new ViewModel( model);
    }
}

Модель:

[Export(typeof(Model))]
public class Model
{
    [ImportMany(typeof(IConnectionSharer))]
    private IEnumerable<IConnectionSharer> AllSharedConnections { get; set; }
    ...
}

Внутренняя реализация IConnectionSharer для плагина "slave":

[Export(typeof(IConnectionSharer))]
public class PrivateConnection : IConnectionSharer
{
    ...
}

Но во время компоновки детали я получаю ошибку при экспорте 'Company. MasterPlugin (ContractName = "IConnectionSharer") "нельзя назначить типу' IConnectionSharer '.

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

UPDATE

Это интересная подсказка - если после очистки моей выходной папки и перестроения решения я удалю плагин "master" (так что плагин "slave" будет использовать свой собственный объект IConnectionSharer ), мое приложение загружается просто отлично, и мой подчиненный плагин также ведет себя как ожидалось. Если я помещаю основной плагин обратно в папку плагинов, у меня снова возникает проблема с композицией MEF.

Я также подумал, что попробую использовать Lazy <>, чтобы посмотреть, будет ли это иметь эффект. Результат был немного поразительным. MEF жалуется на эту ошибку: Невозможно заполнить коллекцию AllSharedConnections, поскольку она не реализует ICollection или доступна только для чтения. Если коллекция не IEnumerable<T> или T [], она должна реализовывать ICollection и быть либо предварительно инициализированной, либо быть доступной для записи с помощью конструктора по умолчанию.

HUH? Очевидно, что AllSharedConnections равно и IEnumerable<T>, так почему MEF жалуется?

Ответы [ 4 ]

3 голосов
/ 23 декабря 2010

Скорее всего, в вашей выходной папке лежит старая версия сборки, например, после переименования.Обычно это не проблема, потому что никакие другие сборки не ссылаются на эту старую.Однако с помощью MEF сборки можно подобрать для композиции, просто присутствуя в папке приложения.

Очистите выходную папку, пересоберите и посмотрите, исчезнет ли проблема.

edit : поскольку проблема не исчезнет, ​​когда вы очистите выходную папку, мое следующее предположение будет, что у вас есть два объявления IConnectionSharer, или вы компилируете тот же кодна две разные сборки.Это приведет к двум IConnectionSharer интерфейсам с тем же именем, но с другим идентификатором.

1 голос
/ 30 декабря 2010

Я выяснил, что стало причиной моей проблемы, но я не уверен, что лучший способ ее решить. Я опубликую решение сейчас, но затем опубликую новый вопрос, потому что оно не имеет никакого отношения к MEF.

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

Мое намерение состояло в том, чтобы позволить "подчиненным" плагинам "заимствовать" соединение у "главных" подключений, и сделать это через интерфейс, который я называю IConnectionSharer. Хорошей новостью является то, что я подтвердил, что это работает - мне просто нужно было выяснить мою ошибку MEF.

Взаимодействие с ключом достигается путем вызова их C DLL с помощью C # DLL (которая использует класс-оболочку C # для вызова в C DLL через PInvoke). Однако в действительности у меня есть два таких ключа, но сторонняя библиотека не позволяет использовать два на компьютере. Я смог заставить его работать, создав еще одну копию моей оболочки C # DLL в виде совершенно отдельной сборки. Эта сборка просто имела ссылки на файлы .cs в моей другой C # DLL, но имела другую копию своего класса оболочки C #. Я изменил эту копию, чтобы вызывать другую копию их C DLL, чтобы заставить ее загружаться по другому адресу в пространстве памяти процесса. До сих пор он работал идеально для нас, без каких-либо проблем со связью в течение многих лет использования.

Теперь, возможно, необходима UML-диаграмма, чтобы указать источник проблемы:

alt text

Как видите, один «ведущий» плагин будет подключаться к одному ключу через DongleConnection, а другой «мастер» плагин будет подключаться к другому ключу через DongleConnection2. Оба класса находятся в одном и том же пространстве имен 1018 *. Плагин «ведомый» должен будет обмениваться данными через один или другой ключ через IConnectionSharer. Он получает доступ к «функции», вызывая RequestFeature().

Проблема в том, что DongleConnection и DongleConnection2 каждый связан с IFeature, который определен в каждой из их соответствующих сборок. Когда я добавил IConnectionSharer, этот интерфейс был также в каждой из этих сборок. Мое приложение загружает оба мастер-плагина, поэтому, когда загружается подчиненный плагин, возникает неопределенность, в которой IFeature запрашивается подчиненным плагином.

По крайней мере, это мое понимание проблемы. Буду признателен за любые комментарии, если вы думаете, что я вводю кого-то в заблуждение своим анализом. После выяснения этого выясняется, что у Вима есть точный ответ (я просто видел его правку после публикации этого ответа, поэтому мне пришлось сменить, кому я присудил ответ!). Я просто не думал об источнике дубликата сборки контракта, потому что магические детали классов DongleConnection и DongleConnection2 были давно забыты. :)

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

1 голос
/ 23 декабря 2010

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

0 голосов
/ 23 декабря 2010

Возможно, у вас есть две разные версии вашей контрактной сборки, и каждая загружается?Один может быть из пути к исполняемому файлу, а другой из пути к плагину.

Это может помочь:

http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx

http://msdn.microsoft.com/en-us/library/dd153782.aspx

...