Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
Далее генерируется (если не была сгенерирована ранее) пара ключей — личный и публичный ключи, с помощью утилиты sn (strong name). Эта пара записывается в файл my. snk.
На следующем этапе компоновщик сборки al (assembly linker) формирует подписанную сборку MyServer. dll, используя модуль MyServer.netmodule и файл с ключами my.snk.
Для регистрации сборки в GAC можно использовать утилиту gacutil.
Для работы
В следующем примере демонстрируется клиент, который может воспользоваться зарегистрированной в GAC сборкой. Кроме этого, в этом примере демонстрируется так называемая динамическая ссылка на сборку.
В случае продемонстрированной в предыдущем примере статической ссылки, которую можно было бы применить и в этом случае (в случае сборки зарегистрированной в GAC), клиент связывается с нужной сборкой на этапе компиляции. В этом случае в манифест сборки клиента записывается вся нужная информация об используемых им сборках. Это позволяет еще до запуска клиента узнать — имеются ли в наличии нужные клиенту компоненты. Благодаря использованию подписанной сборки при ее наличии можно гарантировать ее совместимость с клиентом.
Но иногда нужно связаться со сборкой динамически, в процессе исполнения клиента. В этом случае на этапе компиляции наличие нужной сборки (и нужного типа в данной сборке) не проверяется.
using System;
using System.Reflection;
public class MyApp {
public static void Main {
Assembly assem = Assembly.Load("MyServer, " +
"Version=0.0.0.0, " +
"Culture=neutral, " +
"PublicKeyToken=047772996d01a6d4");
Type accountType = assem.GetType("MyServer.Account");
if (accountType!= null) {
MethodInfо addMethod = accountType.GetMethod("Add");
MethodInfo totalMethod =
accountType.GetMethod("Total");
Object obj = Activator.CreateInstance(accountType);
Object [] args = new Object[1];
args [0] = 3;
if (addMethod!= null){
addMethod.Invoke(obj, args);
args [0] = 5;
addMethod.Invoke(obj, args);
}
if (totalMethod!= null)
Console.WriteLine("Total = {0}",
totalMethod.Invoke(obj, null));
}
}
}
В данном примере статический метод Load класса Assembly загружает из GAC сборку с заданным именем, версией (по умолчанию 0.0.0.0), культурой (по умолчанию neutral) и токеном публичного ключа. Далее, используя механизм рефлексии, получаем ссылку на объект типа Tуре, содержащий всю информацию о классе Account из данной сборки. Если информация об этом типе имеется в загруженной сборке, получаем информацию о методах Add и Total. Потом создаем экземпляр класса Account и формируем массив аргументов. В данном случае массив состоит из одного элемента со значением 3.
Если метод Add реализован в классе Account, вызываем этот метод на построенном объекте, передавая ему сформированный массив аргументов. Модифицируем этот массив, занося в него 5, и еще раз вызываем метод Add.
И, наконец, если метод Total также реализован, вызываем его без аргументов и выводим полученное значение на консоль.
Дополнительный комментарий по поводу обновления версий компонентов. Как было отмечено выше, при использовании статической ссылки на подписанную сборку в манифесте клиента сохраняется хешированное значение сборки, с которой был откомпилирован этот клиент, что делает невозможным его запуск с другой версией этой же сборки. Тем не менее есть возможность связать клиента с более свежей версией сборки без его перекомпиляции. Для этого используются конфигурационные файлы, но их обсуждение находится за пределами данного курса.
Удаленный сервер
До сих пор мы рассматривали компоненты, выполняемые в домене клиентского приложения. Этот режим обеспечивает самую эффективную коммуникацию между клиентом и сервером, но создает проблемы в области надежности и безопасности. Кроме того, часто возникает ситуация, когда с одним компонентом одновременно должны работать различные клиенты. Именно этот случай и рассматривается в следующем примере.
Как и ранее наш сервер принимает вклады, но теперь эти вклады параллельно могут делать различные клиенты. Клиенты и сервер будут запускаться в различных доменах, а для коммуникации через границу доменов будет использоваться механизм каналов.
Сервер
//MyServer.cs
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
namespace MyServer {
public interface IAccumulator {
void Add(int sum);
}
public interface IAudit {
int Total;
}
public class Account: MarshalByRefObject, IAccumulator, IAudit {
protected int _sum = 0;
public void Add(int sum) {
_sum += sum;
}
public int Total {
Console.WriteLine("Server AppDomain = {0}",
AppDomain.CurrentDomain.FriendlyName);
return _sum;
}
}
public class AccountApp {
public static void Main {
HttpChannel myChannel = new HttpChannel (8080);
ChannelServices.RegisterChannel(myChannel);
RemotingConfiguration.RegisterWellKnownServiceType (
typeof(Account),
"Account",
WellKnownObjectMode.Singleton);
Console.WriteLine("Server is listening");
Console.ReadLine;
Console.WriteLine("Bye");