📚 Hub Books: Онлайн-чтение книгРазная литератураИнтернет-журнал "Домашняя лаборатория", 2007 №6 - Вязовский

Интернет-журнал "Домашняя лаборатория", 2007 №6 - Вязовский

Шрифт:

-
+

Интервал:

-
+
1 ... 247 248 249 250 251 252 253 254 255 ... 361
Перейти на страницу:
(производный от System.Object). Если предполагается передача контекста вызова через границу контекста, домена приложения, процесса, машины, значение каждого передаваемого свойства должно быть экземпляром класса, определенного с атрибутом Serializable и производного от ILogicaiThreadAffinative. Определение интерфейса ILogicalThreadAffinative не содержит никаких методов и данный интерфейс используется просто как маркер классов, допускаемых для передачи в контексте вызова за пределы контекста, домена приложения и т. п.

Реально контекст вызова передается через ранее упомянутые границы в форме сообщения типа 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. В результате клиент получает информацию о сборке, содержащей код сервера, выполнившего

1 ... 247 248 249 250 251 252 253 254 255 ... 361
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?