Шрифт:
Интервал:
Закладка:
Реально контекст вызова передается через ранее упомянутые границы в форме сообщения типа IMessage. В связи с этим и требуется сериализуемость всех передаваемых объектов (т. е. возможность их передачи по значению).
Ниже представлен пример, демонстрирующий применение контекста вызова, пересекающего границу между процессами. За основу взят многократно рассмотренный пример, связанный с перечислением 5 условных единиц со стороны клиента на счет, поддерживаемый сервером.
Рассмотрим прежде всего код сервера.
Сервер
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Threading;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
namespace MyServer {
[Serializable]
public class MyCallContextUserName:
ILogicalThreadAffinative {
private String _userName;
public MyCallContextUserName() {
_userName = Environment.UserName;
}
public String UserName {
get { return _userName; }
}
}
[Serializable]
public class MyCallContextServerName:
ILogicalThreadAffinative {
private Assembly _assembly;
private String _serverName;
public MyCallContextServerName() {
_assembly = Assembly.GetExecutingAssembly();
_serverName = _assembly.FullName;
}
public String ServerName {
get { return _serverName; }
}
}
public interface IAccumulator {
void Add(int sum);
}
public interface IAudit {
int Total();
}
[Synchronization()]
public class Account: ContextBoundObject,
IAccumulator, IAudit{
protected int sum = 0;
public void Add(int sum) {
this.sum += sum;
MyCallContextUserName userName =
(MyCallContextUserName)CallContext.GetData("UserName");
Console.WriteLine("UserName = " +
userName.UserName);
CallContext.SetData("ServerName",
new MyCallContextServerName());
}
public int Total() {
return this.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");
}
}
}
Относительно этого кода можно сделать следующие комментарии.
В методе Add наш сервер будет не только получать очередной вклад и зачислять его на счет, но и работать с контекстом вызова.
Во-первых, предполагается, что клиент перед вызовом метода Add добавил в контекст вызова свойство с именем UserName. Соответствующее значение содержит учетные данные пользователя, от имени которого было запущено клиентское приложение. Значение свойства UserName задается ссылкой на экземпляр класса
[Serializable]
public class MyCallContextUserName:
ILogicalThreadAffinative {…}
Из определения этого класса видно, что его экземпляры могут передаваться в контексте вызова за пределы текущего контекста, т. к. этот класс определяется с атрибутом сериализации и наследует интерфейс ILogicalThreadAffinative.
Конструктор данного класса
public MyCallContextUserName() {
_userName = Environment.UserName;
}
сохраняет в строковом поле _userName значение соответствующей переменной среды, получаемой как статическое свойство UserName класса Environment.
Используя свойство userName контекста вызова сервер в методе Add выясняет имя пользователя и выводит его на консоль:
MyCallContextUserName userName =
(MyCallContextUserName)CallContext.GetData("UserName");
Console.WriteLine("UserName = " +
userName.UserName);
Для доступа к нужному свойству используется статический метод GetData класса CallContext, которому в качестве параметра передается имя свойства. Полученное значение приводится к типу MyCallContextUserName.
Во-вторых, получив и выведя на консоль имя пользователя, сервер заканчивает выполнение метода Add, включая в контекст вызова свою информацию. Эту новую информацию сможет получить клиент, дождавшийся возврата из метода Add.
Итак, сервер добавляет в контекст вызова новое свойство с именем ServerName:
CallContext.SetData("ServerName",
new MyCallContextServerName()};
Класс MyCallContextS erverName определяется аналогично классу MyCallContextUserName.
Основная функциональность этого класса определяется его конструктором:
public MyCallContextServerName() {
_assembly = Assembly.GetExecutingAssembly();
_serverName = _assembly.FullName;
}
Здесь мы получаем ссылку на исполняемую сборку (т. е. на сборку сервера) и сохраняем в _serverName ее полное имя.
Остальная часть кода сервера не претерпела каких-либо изменений.
Клиент
using System; using MyServer;
using System. Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Net;
using System.Runtime.Remoting.Messaging;
public class MyApp {
public static void Main() {
HttpChannel с = new HttpChannel();
ChannelServices.RegisterChannel(c);
try {
Account a = (Account)Activator.GetObject(typeof(Account),
"http://localhost:8080/Account",
WellKnownObjectMode.Singleton);
CallContext.SetData("UserName",
new MyCallContextUserName());
a. Add(5);
Console.WriteLine("5 is sent to " +
((MyCallContextServerName)CallContext.GetData(
"ServerName")).ServerName);
Console.WriteLine("Total = " +a.Total());
}
catch(WebException e) {
Console.WriteLine(e.Message);
}
catch(Exception e) {
Console.WriteLine(e.Message);
}
finally!
Console.WriteLine("Bye");
}
}
}
Все отличие данного кода клиента от рассмотренных ранее примеров представлено в следующих строках
CallContext.SetData("UserName",
new MyCallContextUserName());
a. Add(5);
Console.WriteLine("5 is sent to " +
((MyCallContextServerName)CallContext.GetData (
"ServerName")).ServerName);
Перед вызовом метода Add клиент добавляет в контекст вызова уже рассмотренное свойство userName. В результате, при выполнении вызова Add, сервер получает доступ к учетным данным пользователя, от имени которого было запущено клиентское приложение, и может принимать решение о выполнении данного вызова.
По завершении вызова метода Add клиент получает из свойства контекста значение свойства serverName, добавленное в контекст вызова сервером при выполнении Add. В результате клиент получает информацию о сборке, содержащей код сервера, выполнившего