Создание бизнес-приложений с помощью Silverlight - Разбиение приложений на разделы
ОГЛАВЛЕНИЕ
Разбиение приложений на разделы
Одним из факторов, влияющих на длительность запуска приложения Silverlight, является размер исходного пакета. Рекомендации относительно размера пакета XAP не отличаются от рекомендаций по объему страницы для веб-приложений. Пропускная способность является ограниченным ресурсом. Строгие требования ко времени ответа веб-приложений требуют пристального внимания к длительности запуска приложения Silverlight.
Помимо времени на обработку, проходящего до отображения первого UserControl, на эту важную характеристику приложения непосредственно влияет объем пакета приложения. Для повышения скорости запуска необходимо избегать монолитных файлов XAP, размер которых может достигать десятков мегабайт в случае сложных приложений.
Приложение Silverlight можно разбить на коллекцию файлов XAP; отдельные библиотеки DLL; или отдельные файлы XML, изображения и файлы других типов с известными MIME. Для демонстрации мелкомасштабного разбиения приложения центра обработки вызовов на разделы я буду использовать элемент управления OrderDetail Silverlight в качестве отдельной библиотеки DLL (AdvOrderClientControls.dll) наряду с файлом AdvCallCenterClient.xap в каталоге ClientBin проекта AdvCallClientWeb (обратитесь к рис. 1).
Библиотека DLL будет заранее загружаться в рабочий поток, когда агент принимает входящий вызов. За это отвечает вызов, который вы видели на рис. 4, ThreadPool.QueueUserWorkItem(ExecuteControlDownload). После того, как вызывающая сторона ответит на вопросы, относящиеся к безопасности, я использую отражение для создания элемента управления OrderDetail и добавляю его к дереву элементов управления, прежде чем отображать его на экране. На рис. 14 показан элемент управления OrderDetail.xaml с данными заказа, загруженный в дерево элементов управления.
Библиотека DLL, содержащая элемент управления OrderDetail, разворачивается на том же веб-сайте, что и клиент центра обработки вызовов. Это характерно для библиотек DLL, принадлежащих одному и тому же приложению, поэтому в данном случае не возникает никаких проблем, связанных с разными доменами. Однако они могут возникнуть при работе со службами. Приложения Silverlight могут осуществлять доступ к службам, развернутым в нескольких доменах, включая локальные и из «облака», как показано на схеме архитектуры (и снова следует обратиться к рис. 1).
Метод ExecuteControlDownload (см. рис. 4) выполняется в фоновом рабочем потоке и использует класс WebClient для загрузки DLL. В WebClient по умолчанию предполагается, что загрузка выполняется из домена происхождения и, следовательно, используются только относительные URI.
Обработчик OrderDetailControlDownloadCallback принимает поток DLL и создает сборку с помощью ResourceUtility.GetAssembly(), который показан на рис. 15. Поскольку создание сборки должно происходить в потоке пользовательского интерфейса, я отправляю GetAssembly() и (безопасное с точки зрения потока) присваивание сборки глобальной переменной для потока пользовательского интерфейса:
void OrderDetailControlDownloadCallback(object sender, OpenReadCompletedEventArgs e)
{
this.Dispatcher.BeginInvoke(delegate() {
Assembly asm = ResourceUtility.GetAssembly(e.Result);
Interlocked.Exchange<Assembly>(ref
ClientGlobals.advOrderControls_dll, asm ); });
}
Рис. 15 Функции вспомогательной программы для извлечения ресурсов
public class ResourceUtility
{
//helper function to retrieve assembly from a package stream
public static Assembly GetAssembly(string assemblyName, Stream
packageStream)
{
StreamResourceInfo srInfo =
Application.GetResourceStream(
new StreamResourceInfo(packageStream, "application/binary"),
new Uri(assemblyName, UriKind.Relative));
return GetAssembly(srInfo.Stream);
}
//helper function to retrieve assembly from a assembly stream
public static Assembly GetAssembly(Stream assemblyStream)
{
AssemblyPart assemblyPart = new AssemblyPart();
return assemblyPart.Load(assemblyStream);
}
//helper function to create an XML document from the stream
public static XElement GetXmlDocument(Stream xmlStream)
{
XmlReader reader = XmlReader.Create(xmlStream);
XElement element = XElement.Load(reader);
return element;
}
//helper function to create an XML document from the default package
public static XElement GetXmlDocumentFromXap(string fileName)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = new XmlXapResolver();
XmlReader reader = XmlReader.Create(fileName);
XElement element = XElement.Load(reader);
return element;
}
//gets the UIElement from the default package
public static UIElement GetUIElementFromXaml(string xamlFileName)
{
StreamResourceInfo streamInfo = Application.GetResourceStream(new
Uri(xamlFileName, UriKind.Relative));
string xaml = new StreamReader(streamInfo.Stream).ReadToEnd();
UIElement uiElement = null;
try
{
uiElement = (UIElement)XamlReader.Load(xaml);
}
catch
{
throw new SLApplicationException(string.Format("Can't create
UIElement from {0}", xamlFileName));
}
return uiElement;
}
}
Поскольку отправленный делегат и обработчик обратного вызова выполняются в разных потоках, необходимо отдавать себе отчет в том, что доступ к объектам осуществляет анонимный делегат. В предыдущем коде состояние загружаемого потока DLL имело большое значение. Вы не можете написать код, запрашивающий ресурсы потока, в функции OrderDetailControlDownloadCallback. Такой код будет преждевременно избавляться от загруженного потока до того, как поток пользовательского интерфейса получит шанс создать сборку. Я использую отражение для создания экземпляра пользовательского элемента управления OrderDetail и добавлю его на панель, как показано ниже.
_orderDetailContol = ClientGlobals.advOrderControls_dll.CreateInstance
("AdvOrderClientControls.OrderDetail") as UserControl;
spCallProgressPanel.Children.Add(_orderDetailContol);
ResourceUtility на рис. 15 также демонстрирует различные функции вспомогательной программы, предназначенные для извлечения UIElement из документа XAML иXML, входящего в загруженные потоки и пакеты по умолчанию.