Платформа MEF (
eng/rus) является каркасом системы Ultima Businessware®. Она собирает приложение из независимых компонентов, в качестве которых выступают ядерные службы, скрипты и прикладные классы. MEF используется и в серверной, и в клиентской частях, благодаря чему программа в обоих случаях имеет одинаковую модульную структуру и использует единый API, хорошо документированный и достаточно популярный.
MEF представляет собой систему позднего связывания компонентов, основанную на правилах сопоставления. Сопоставление происходит между так называемыми импортами и экспортами, предоставляемыми компонентами. Чтобы некоторый компонент заработал, всем его импортам должны быть сопоставлены экспорты других компонентов. Связывание происходит во время выполнения программы, что обеспечивает необходимую гибкость. Компоненты, из которых собирается программа, могут разрабатываться параллельно разными командами программистов: скажем, форма клиентского приложения может использовать серверную службу, которая находится в процессе разработки, при условии, что интерфейс этой службы уже формально описан.
Обратной стороной такой гибкости являются ошибки связывания. Если во время выполнения приложению потребуется служба, которая еще не реализована, возникнет именно такая ошибка. Компоненты программы могут иметь сложные зависимости, и если хотя бы одна из зависимостей не найдена, целый компонент оказывается непригоден к использованию.
К сожалению, диагностика подобных ошибок затруднена. Все, о чем знает MEF на момент возникновения ошибки связывания – это то, что одно из правил связывания не может быть соблюдено. Типичный текст ошибки в этом случае выглядит так:
The composition produced a single composition error. The root cause is provided below. Review the CompositionException. Errors property for more detailed information.
1) No exports were found that match the constraint: ContractName Ultima.Scripting.IUserCommand RequiredTypeIdentity Ultima.Scripting.IUserCommand RequiredMetadata ScriptID (Ultima.Scripting.IScriptMetadata)
Resulting in: Cannot set import ' ContractName Ultima.Scripting.IUserCommand RequiredTypeIdentity Ultima.Scripting.IUserCommand RequiredMetadata ScriptID (Ultima.Scripting.IScriptMetadata)' on part '(name)'
Element: ContractName Ultima.Scripting.IUserCommand RequiredTypeIdentity Ultima.Scripting.IUserCommand RequiredMetadata ScriptID (Ultima.Scripting.IScriptMetadata) --> Unknown Origin
at System.ComponentModel.Composition.CompositionResult.ThrowOnErrors(AtomicComposition atomicComposition) at System.ComponentModel.Composition.Hosting.ImportEngine.SatisfyImportsOnce(ComposablePart part) at System.ComponentModel.Composition.Hosting.CompositionContainer.SatisfyImportsOnce(ComposablePart part) |
Из текста ошибки понятно только, что какой-то компонент не может быть предоставлен по требованию системы. Какой именно компонент вызвал ошибку и как эту ошибку исправлять – не ясно.
Поскольку интерес представляют только прикладные ошибки подобного рода, круг возможных причин ограничен двумя типичными ситуациями:
•ошибка в каком-нибудь скрипте (не обязательно том, который запрашивался);
•ошибка в какой-нибудь клиентской форме (или одной из ее зависимостей).
Для разбора подобных ошибок на основе консольной утилиты Mefx был разработан инструмент MEF Explorer. Он показывает структуру каталогов MEF клиентской и серверной частей (на соответствующих закладках). |
Если ошибок нет, все импорты компонентов сопоставлены экспортам каких-нибудь других компонентов, и у всех компонуемых частей отображается статус Accepted.
Больший интерес, разумеется, представляют другие статусы. Любая ошибка связывания означает, что компонент был отвергнут по одной из причин:
Primary Rejection – отсутствие для некоторых импортов компонента соответствующих экспортов; |
Rejected – экспорты компонента присутствуют, но не могут быть созданы из-за того, что для их импортов, в свою очередь, отсутствуют необходимые экспорты. |
Рассмотрим действия прикладного разработчика при возникновении ошибки.
Предположим, ошибка связывания (No exports were found that match the constraint...) происходит при запросе MobileStorePickupService. Настоящая проблема, однако, заключена не в нем. Если открыть скрипт в редакторе, можно убедиться, что он компилируется без ошибок. Для поиска причины следует запустить MEF Explorer и поискать в списке Composable parts по статусу Rejected или Primary Rejection и по имени компонент, вызвавший ошибку.
При выборе этого компонента в списке Imports справа от него будут показаны все импорты, как связанные, так и не связанные с экспортами. При осмотре несвязанных импортов станет ясна реальная причина возникшей ошибки:
В приведенном примере видно, что проблема заключается IStoreZonePickupService. Эта прикладная служба отсутствует в каталоге на стороне сервера. Возможны такие причины ее отсутствия:
•для службы создали интерфейс, но еще не написали реализацию;
•реализация есть, но имеет ошибки компиляции;
•MEF Cache у скрипта службы по какой-то причине пуст (возможно, в результате слияния версий метаданных).
Для исправления обнаруженной ошибки необходимо:
•найти интерфейс IStoreZonePickupService в справочнике Interfaces:
•проверить, есть ли реализация для этого интерфейса (если нет, добавить):
•если реализация есть, следует исправить ее скрипт: