Добавить в цитаты Настройки чтения

Страница 230 из 372

Реально контекст вызова передается через ранее упомянутые границы в форме сообщения типа IMessage. В связи с этим и требуется сериализуемость всех передаваемых объектов (т. е. возможность их передачи по значению).

Ниже представлен пример, демонстрирующий применение контекста вызова, пересекающего границу между процессами. За основу взят многократно рассмотренный пример, связанный с перечислением 5 условных единиц со стороны клиента на счет, поддерживаемый сервером.

Рассмотрим прежде всего код сервера.

Сервер

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Cha

using System.Runtime.Remoting.Cha

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() {

            HttpCha

            Cha





            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.Cha

using System.Runtime.Remoting.Cha

using System.Net;

using System.Runtime.Remoting.Messaging;

public class MyApp {

       public static void Main() {

           HttpCha

           Cha

           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());