Я работаю над реализацией динамической загрузки страниц в существующее приложение Silverlight. Все идет нормально. Работает как импорт страниц из работы внешнего .xap, так и экспорт данных из хост-приложения в плагины.
Хост-приложение использует веб-сервис, который предоставляет несколько типов, относящихся к бэкэнд-системе. Мой вопрос: как мне экспортировать объекты типов, определенных в веб-сервисе?
Пример. I Экспорт списка SMS_SupportedPlatforms, который определен в клиенте WS. Это из хостинг-приложения. Свойство находится в App.xaml.cs основного приложения Silverlight. Он заполняется асинхронным вызовом веб-службы.
[Export(ExportContracts.SMS_SupportedPlatforms)]
public static List<Client.SMS_SupportedPlatforms> SupportedPlatforms = new List<Client.SMS_SupportedPlatforms>();
Отредактировано:
Я переместил Imports в отдельный класс во внешних сборках. Теперь они читают в этом классе:
public class NeoServiceManagerImports
{
[Import(NeoSMExportContracts.DepartmentExportAttribute, AllowRecomposition = true)]
public string Department { get; set; }
[Import(NeoSMExportContracts.RolesExportAttribute, AllowRecomposition = true)]
public List<string> Roles { get; set; }
[Import(NeoSMExportContracts.SMS_SupportedPlatforms, AllowRecomposition = true)]
public List<Client.SMS_SupportedPlatforms> SupportedPlatforms
{
get;
set;
}
public NeoServiceManagerImports()
{
CompositionInitializer.SatisfyImports(this);
}
*
} * 1013
Этот класс вызывается конструктором страницы для экспорта (в целях тестирования).
Затем страница экспортируется с атрибутом MetadataAttribute в класс UIProviderbase, который я предложил для экспорта плагинов (игнорируйте очень абстрактное именование ;-)) В классе есть несколько реквизитов для логотипа, названия и списка страниц.
[Export(typeof(UIProviderBase))]
public class ExternalMainMenuExternalSubMenuUIProvider: UIProviderBase
{
public override string Title
{
get { return "Submenu"; }
}
public override string ImageUri
{
get { return "uriuri"; }
}
[ImportMany("ExternalSubMenuForExternalMainMenuContract")]
public override List<System.ComponentModel.Composition.ExportFactory<FrameworkElement, IPageMetadata>> EntryPage
{
get;
set;
}
}
Я уверен, что проблема связана с тем, что MEF не может разрешить тип как один и тот же, когда на него ссылаются две разные сборки. Есть ли способ решить эту проблему, без рефакторинга хост-приложения, чтобы получить список ISMS_SupportedPlatforms? В настоящее время кажется, что приложение хостинга экспортируется правильно, но оно никогда не обнаруживается в плагине.
Если у меня AllowDefault = true, страница загружается, но SupportedPlatforms остается нулевым. Если false, он не экспортирует страницу и молча завершается неудачей.
Я немного изменил способ загрузки страниц и пытаюсь получить больше информации для вас. Это ошибка, которую я вижу сейчас
The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) No valid exports were found that match the constraint '((exportDefinition.ContractName == "SMS_SupportPlatformsExport") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "System.Collections.Generic.List(HelloWorld.MEF.Client.SMS_SupportedPlatforms)".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.
Resulting in: Cannot set import 'HelloWorld.MEF.NeoServiceManagerImports.SupportedPlatforms (ContractName="SMS_SupportPlatformsExport")' on part 'HelloWorld.MEF.NeoServiceManagerImports'.
Element: HelloWorld.MEF.NeoServiceManagerImports.SupportedPlatforms (ContractName="SMS_SupportPlatformsExport") --> HelloWorld.MEF.NeoServiceManagerImports
Resulting in: An exception occurred while trying to create an instance of type 'HelloWorld.MEF.ExternalMainMenuExternalSubMenu'.
Resulting in: Cannot activate part 'HelloWorld.MEF.ExternalMainMenuExternalSubMenu'.
Element: HelloWorld.MEF.ExternalMainMenuExternalSubMenu --> HelloWorld.MEF.ExternalMainMenuExternalSubMenu --> AssemblyCatalog (Assembly="HelloWorld.MEF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
Resulting in: Cannot get export 'HelloWorld.MEF.ExternalMainMenuExternalSubMenu (ContractName="ExternalSubMenuForExternalMainMenuContract")' from part 'HelloWorld.MEF.ExternalMainMenuExternalSubMenu'.
Element: HelloWorld.MEF.ExternalMainMenuExternalSubMenu (ContractName="ExternalSubMenuForExternalMainMenuContract") --> HelloWorld.MEF.ExternalMainMenuExternalSubMenu --> AssemblyCatalog (Assembly="HelloWorld.MEF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
Это код, где появляется исключение
var page = (from p in this.Plugins
from e in p.EntryPage
where e.Metadata.NavigateUri == this.targetUri.ToString()
select e).Single().CreateExport().Value;
Проживание в этом классе (загрузка страниц динамически)
public class MefContentLoader : INavigationContentLoader
{
private PageResourceContentLoader pageResourceContentLoader = new PageResourceContentLoader();
private Uri targetUri;
[ImportMany(AllowRecomposition = true)]
public UIProviderBase[] Plugins
{
get;
set;
}
public MefContentLoader()
{
CompositionInitializer.SatisfyImports(this);
}
#region INavigationContentLoader Members
public IAsyncResult BeginLoad(Uri targetUri, Uri currentUri, AsyncCallback userCallback, object asyncState)
{
this.targetUri = targetUri;
return pageResourceContentLoader.BeginLoad(targetUri, currentUri, userCallback, asyncState);
}
public bool CanLoad(Uri targetUri, Uri currentUri)
{
// TODO: Handle this properly
return true;
}
public void CancelLoad(IAsyncResult asyncResult)
{
// TODO: Handle this properly
pageResourceContentLoader.CancelLoad(asyncResult);
}
public LoadResult EndLoad(IAsyncResult asyncResult)
{
if (this.Plugins.Length == 0 ||
this.Plugins.Count(p => p.EntryPage != null && p.EntryPage.Any(u => u.Metadata.NavigateUri == targetUri.ToString())) == 0)
{
return pageResourceContentLoader.EndLoad(asyncResult);
}
var page = (from p in this.Plugins
from e in p.EntryPage
where e.Metadata.NavigateUri == this.targetUri.ToString()
select e).Single().CreateExport().Value;
return new LoadResult(page);
}
#endregion
}
Подводя итог:
В сборке B (плагин) я экспортирую несколько страниц в UIProviderbase в сборке B. Страницы должны использовать данные из сборки A (основное приложение Silverlight). Сборка A затем должна импортировать UIProviderbase в виде плагинов, и из них получить коллекцию страниц меню, добавляя их в приложение.
Это работает со всеми, кроме типа, определенного в веб-сервисе, на который ссылаются обе сборки A и B.