Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / Hungry Minds - C# Bible

.pdf
Скачиваний:
226
Добавлен:
12.02.2016
Размер:
4.21 Mб
Скачать

using System;

using System.Data.SqlClient; using System.EnterpriseServices;

[Transaction(TransactionOption.Required)] public class ExAirMain : ServicedComponent

{

public void Process()

{

/* call methods to add Food info and Ticket info */

AddFood process1 = new AddFood(); AddAirline process2 = new AddAirline(); process1.Add();

process2.Add();

}

}

[Transaction(TransactionOption.Supported)]

[AutoComplete]

public class AddFood : ServicedComponent

{

public void Add()

{

SQLConnection cnn = new SQLConnection("FoodSupplierConnection");

SQLCommand cmd = new SQLCommand(); cnn.Open();

cmd.ActiveConnection = cnn;

cmd.CommandText = ""; // Insert statement to DB cmd.ExecuteNonQuery();

cnn.Close();

}

}

[Transaction(TransactionOption.Supported)]

[AutoComplete]

public class AddAirline : ServicedComponent

{

public void Add()

{

SQLConnection cnn = new SQLConnection("AirlineConnection"); SQLCommand cmd = new SQLCommand(); cnn.Open();

cmd.ActiveConnection = cnn;

cmd.CommandText = "" // Insert statement to DB cmd.ExecuteNonQuery();

cnn.Close();

}

}

}

Accessing Object Context

The System.EnterpriseServices namespace includes a class called ContextUtil, which can be used by C# classes to access an object's COM+ runtime context. In Visual Basic 6, you

accessed the object context of the current component through the ObjectContext object, as the following code demonstrates:

Dim ctx as ObjectContext ctx = GetObjectContext

The ContextUtil class contains several properties and methods that give callers access to COM+ context state information. All of the methods and properties in the class are static, which means that you can access the members directly from the ContextUtil class without creating an object of the class. Table 35-3 describes the properties of the ContextUtil class, and Table 35-4 describes the methods of the ContextUtil class.

 

Table 35-3: ContextUtil Class Properties

 

 

 

 

 

 

Property

 

 

 

 

Description

 

 

 

 

 

 

ActivityId

 

 

 

 

Gets a GUID representing the activity containing the

 

 

 

 

 

component

 

 

 

 

 

 

ApplicationId

 

 

 

 

Gets a GUID for the current application

 

 

 

 

 

 

ApplicationInstanceId

 

 

 

 

Gets a GUID for the current application instance

 

 

 

 

 

 

ContextId

 

 

 

 

Gets a GUID for the current context

 

 

 

 

 

 

DeactivateOnReturn

 

 

 

 

Gets or sets the done bit in the COM+ context

 

 

 

 

 

 

IsInTransaction

 

 

 

 

Gets a value indicating whether the current context is

 

 

 

 

 

transactional

 

 

 

 

 

 

IsSecurityEnabled

 

 

 

 

Gets a value indicating whether role-based security is active in

 

 

 

 

 

the current context

 

 

 

 

 

 

MyTransactionVote

 

 

 

 

Gets or sets the consistent bit in the COM+ context

 

 

 

 

 

 

PartitionId

 

 

 

 

Gets a GUID for the current partition

 

 

 

 

 

 

Transaction

 

 

 

 

Gets an object describing the current COM+ DTC transaction

 

 

 

 

 

 

TransactionId

 

 

 

 

Gets the GUID of the current COM+ DTC transaction

 

 

 

 

 

Table 35-4: ContextUtil Class Properties

 

 

 

 

Property

 

 

Description

 

 

 

 

DisableCommit

 

 

Sets both the consistent bit and the done bit to False in the COM+

 

 

 

context

 

 

 

 

EnableCommit

 

 

Sets the consistent bit to True and the done bit to False in the

 

 

 

COM+ context

 

 

 

 

GetNamedProperty

 

 

Returns a named property from the COM+ context

 

 

 

 

IsCallerInRole

 

 

Determines whether the caller is in the specified role

 

 

 

 

SetAbort

 

 

Sets the consistent bit to False and the done bit to True in the

 

 

 

COM+ context

 

 

 

 

SetComplete

 

 

Sets the consistent bit and the done bit to True in the COM+

 

 

 

context

 

 

 

 

 

 

The code in Listing 35-4 implements a transactional COM+ component that implements a public method called DoWork(). The DoWork() method checks the IsCallerInRole() property

to determine the caller's COM+ role. If the caller's role is the ClientRole role, then the object's transaction is committed with a call to SetComplete(). If the caller's role is a role other than the ClientRole role, then the object's transaction is rolled back with a call to SetAbort().

Listing 35-4: Accessing COM+ Context Through the ContextUtil Class

using System.Reflection;

using System.EnterpriseServices;

[assembly:AssemblyKeyFile("keyfile.snk")] [assembly:AssemblyVersion("1.0.*")]

[ObjectPooling(5, 10)] [Transaction(TransactionOption.Required)] [SecurityRole("ClientRole")]

public class PooledClass : ServicedComponent

{

public PooledClass()

{

}

~PooledClass()

{

}

public override bool CanBePooled()

{

return true;

}

public override void Activate()

{

}

public override void Deactivate()

{

}

public void DoWork()

{

bool IsInRole;

IsInRole = ContextUtil.IsCallerInRole("ClientRole"); if(IsInRole == true)

ContextUtil.SetComplete(); else

ContextUtil.SetAbort();

}

}

Summary

Exposing your C# class as a COM+ application takes very little effort and is certainly easier than implementing the same functionality using earlier versions of Visual Studio 6.0. COM+ applications written using Visual C++ 6.0 needed much more code to complete the same tasks, and some COM+ features (such as object pooling) were not even available to COM+ components written in Visual Basic 6.0.

Developing COM+ components using C# involves only four simple concepts:

Derive your class from ServicedComponent

Add attributes to describe your application's settings

Use the regsvcs tool to build a COM+ application for your public classes

Call methods and properties on the ContextUtil class to access the COM+ context at runtime.

Microsoft dropped hints about this COM+ programming model as far back as 1997. Back then, they described a model based on attributed programming, in which COM components could be described with attributes and the runtime would take over details such as class factories and IUnknown-style reference counting. It is now clear that the .NET model of COM+ component development is the fulfillment of that original vision.

Chapter 36: Working with .NET Remoting

In This Chapter

The .NET Framework provides several mechanisms that enable you to write applications that do not exist in the same application domain, server process, or machine. Based on the requirements of your application, such as the capability of non-.NET servers to access your data, you can choose any of the different types of object communication methods. In this chapter, you learn about .NET remoting. Using remoting, you can marshal objects and method calls across process boundaries and effectively pass data between applications.

In earlier chapters, you learned that ASP.NET and XML Web services were also excellent ways to pass objects and data between process boundaries, but depending on your application infrastructure, those services may not be the best option available. Remoting fills in any of the gaps that were left open by these services. In this chapter, you learn how to implement remoting, how to create the client and server objects in a remoting framework, and how to effectively pass data using remoting across process boundaries.

Introducing Remoting

.NET remoting enables applications to communicate between objects that reside on different servers, different processes, or different application domains. Before the .NET Framework was introduced, you could pass objects across process boundaries by using distributed COM, or DCOM. DCOM worked well, but it had limitations, such as the types of data that could be passed, and security context passed between the client caller and server activation. Moreover, it was based on COM, which meant that although you could communicate across machine boundaries, all machines had to be running a Microsoft operating system. This isn't a critical limitation, but it limited your options regarding what you could do with your existing infrastructure. In .NET, remoting takes care of these issues, and expands upon what DCOM offered as a viable method to remotely communicate between objects.

With remoting, you implement a server, or host application, and a client application. On the host or client, the application can be any of the .NET application templates that are available, including console applications, Windows services applications, ASP.NET applications,

WindowsForms applications, and IIS applications. On the host, you programmatically configure — or use a configuration file to specify — the type of activation that will be allowed by clients. Clients can use one of several types of activation methods, including Singleton and SingleCall, which is covered in the section "Activating the Remote Object," later in this chapter. It is at this point that you specify the channel and the port over which the object communicates, and the format the data will have when it is passed between host and client. You learn how channels and ports are implemented a little bit later. The format of the data is important, based on the system design; you can use binary data, SOAP, or a custom format to marshal the data. After you specify the channel, port, and format, based on the type of remoting host you are exposing, you need to determine how to expose the metadata to the clients. You can do this in several ways, such as by allowing the caller to download the assembly, or by making the source available to the caller. Either way, the client needs to know what object it is creating, so the metadata in some form needs to be made available to the caller. After the host is properly built and configured, you then write the client. On the client, all you need to do is create an instance of the object on the specified channel and port that is expecting requests on the server. You accomplish this programmatically or through a configuration file. At this point, the method calls are no different from any other object that you consume from a .NET application. After you create objects, you call methods, set and retrieve properties, and fire off events just as you would with an object that is not using the remoting framework.

This may seem like a lot of steps, but it is actually very simple after you have done it once. You can break down the overall process into the following broader tasks, which are illustrated in Figure 36-1.

Figure 36-1: .NET remoting overview

1.Specify the channels and ports that marshal objects between the host and the client.

2.Use formatters (which you will learn about later in the chapter) to specify the format in which the data is serialized and deserialized in between the host and the client.

3.Determine how the host objects are activated and how long the activation lasts.

In the following sections, you learn how to create the host application in a remoting scenario, including the specifics of formatters, channels, and ports, and how the host will be activated. After the host is built, you learn how to consume the remote object from a client application.

Creating a Remoting Host Assembly

To begin your remoting application, you need to create an assembly containing the actual method calls that the host application will use. Once you have created the assembly, you then

create the host application that accepts client requests for the methods in the assembly. In the following steps, you build the assembly that implements the methods to be called:

1.Create a new C# Class Library application and call it HostObject. For simplicity, I have created a directory on my C drive called cSharpRemoting, and have added three subfolders named Host, HostObject, and Client. You might see where we are going with this. The HostObject Class Library application should be created in the cSharpRemoting\HostObject directory. This makes is easier for you to run the console applications you create later.

2.After you create the HostObject class library application, you add a public method that accepts a parameter, called customerID, and returns the name of the customer from the Northwind database from SQL Server based on the customerID passed in. Your completed class for the HostObject application should look something like the one shown in Listing 36-1.

Listing 36-1: Creating the Host Object Application

using System; using System.Data;

using System.Data.SqlClient;

namespace HostObject

{

public class Class1: MarshalByRefObject

{

public string thisCustomer;

public Class1()

{

Console.WriteLine("HostObject has been activated");

}

public string ReturnName(string customerID)

{

// Create connection, command object to SQL

string cnStr = "Initial Catalog=Northwind;Data" + "Source=localhost;Integrated Security=SSPI;";

SqlConnection cn = new SqlConnection(cnStr);

string strSQL =

("Select CompanyName from Customers " +

" where CustomerID = '" + customerID + "'");

SqlCommand cmd = cn.CreateCommand();

cmd.CommandText = strSQL;

cn.Open();

SqlDataReader rdr = cmd.ExecuteReader (CommandBehavior.CloseConnection);

while (rdr.Read())

{

thisCustomer = rdr.GetString(0);

}

Console.WriteLine(thisCustomer + " was returned to the client");

return thisCustomer;

}

}

}

The preceding code performs a simple query to SQL Server to grab the CompanyName field in the Customers database based on the parameter customerID, which is passed into the method. As you can see, this code is no different from any other class library that you would create in C#. The next step is to create the host application that services the client requests for this class library.

Creating the Remoting Server

To create the application that will host the HostObject assembly, which is where you actually start to use some of the remoting features created in Listing 36-1, you need to create a console application called Host in the C:\cSharpRemoting\Host directory. This host application is the actual remoting server that uses the features of the System.Runtime.Remoting namespace.

Before you start any coding, several key features of remoting need to be described. The namespace that contains the remoting functionality is the System.Runtime. Remoting namespace, whose classes are described in Table 36-1. Although you do not use all of these classes when writing remoting applications, several of the classes are extremely important to inplementing a remoting infrastrucure; namely, the ObjRef class, RemotingConfiguration class, the RemotingServices class, and the WellKnownObjectMode enumeration. You learn more about each of these in detail later in this section as you write the code for your host application.

Table 36-1: System.Runtime.Remoting Classes

Class

 

Description

 

 

 

ActivatedClientTypeEntry

 

Holds values for an object type registered on the

 

 

client end as a type that can be activated on the server

 

 

 

ActivatedServiceTypeEntry

 

Holds values for an object type registered on the

 

 

service end as one that can be activated on request

 

 

from a client

 

 

 

ObjectHandle

 

Wraps marshal by value object references, enabling

 

 

them to be returned through an indirection

 

 

 

ObjRef

 

Stores all relevant information required to generate a

 

 

proxy to communicate with a remote object

 

 

 

RemotingConfiguration

 

Provides various static methods for configuring the

 

 

remoting infrastructure

 

 

 

RemotingException

 

The exception that is thrown when something has

 

 

gone wrong during remoting

 

Table 36-1: System.Runtime.Remoting Classes

Class

 

 

Description

RemotingServices

Provides several methods for using and publishing remoted objects and proxies. This class cannot be inherited

RemotingTimeoutException

The exception that is thrown when the server or the client cannot be reached for a previously specified period of time

ServerException

The exception that is thrown to communicate errors to the client when the client connects to non-.NET Framework applications that cannot throw exceptions

SoapServices

TypeEntry

Provides several methods for using and publishing remoted objects in SOAP format

Implements a base class that holds the configuration information used to activate an instance of a remote type

WellKnownClientTypeEntry

WellKnownServiceTypeEntry

Holds values for an object type registered on the client as a well-known type object (single call or singleton)

Holds values for an object type registered on the service end as a well-known type object (single call or singleton)

To begin writing the host application, you need to understand what the remoting infrastructure needs to operate. To refresh your memory regarding the steps you need to take to create the host application, review the following three steps outlined earlier in the chapter:

1.Specify the channels and ports that marshal objects between the host and the client.

2.Use formatters to specify the format in which the data is serialized and deserialized in between the host and the client.

3.Determine how the host objects are activated, and how long the activation lasts.

The following sections examine each one of these steps.

Specifying channels and ports

In the remoting infrastructure, channels handle the transporting of messages, or data, between the client and the server objects. Recall what is actually happening when you are remoting objects: You are crossing a boundary, such as an application domain, a server process, or a physical machine. The specific channel that you provide handles all of the underlying details of getting the data to and from the remote objects; you simply specify a channel type and all of the dirty work is done for you. The System.Runtime.Remoting.Channels class provides the implementations for creating the channels that are used in your remoting host. When you register a channel in your application, you must make sure that it is registered before you attempt to access the remote objects. Failing to correctly register your channels causes an error. If another application is listening on a channel that you are attempting to listen on, an error occurs and your host application fails to load. You need to know which channels are

being used, and which channel your application needs to use, based on what the client is calling on. After you declare an instance of the channel type that you are going to use, you call the RegisterChannel() method of the ChannelServices class, which registers the channel for use. Table 36-2 describes the methods of the ChannelServices class that are available to you.

Table 36-2: ChannelServices Class Methods

Method

 

Description

 

 

 

AsyncDispatchMessage

 

Asynchronously dispatches the given message to the

 

 

server-side chain(s) based on the URI embedded in

 

 

the message

 

 

 

CreateServerChannelSinkChain

 

Creates a channel sink chain for the specified

 

 

channel

 

 

 

DispatchMessage

 

Dispatches incoming remote calls

 

 

 

GetChannel

 

Returns a registered channel with the specified name

 

 

 

GetChannelSinkProperties

 

Returns an IDictionary of properties for a given

 

 

proxy

 

 

 

GetUrlsForObject

 

Returns an array of all the URLs that can be used to

 

 

reach the specified object

 

 

 

RegisterChannel

 

Registers a channel with the channel services

 

 

 

SyncDispatchMessage

 

Synchronously dispatches the incoming message to

 

 

the server-side chain(s) based on the URI embedded

 

 

in the message

 

 

 

UnregisterChannel

 

Unregisters a particular channel from the registered

 

 

channels list

Not listed in this table is a property in the ChannelServices class called RegisteredChannels, which sets or gets the registered channels in the current object instance.

The following code snippet creates and registers a TCP and HTTP channel on specific ports using the RegisterChannel method of the ChannelServices class:

TcpChannel chan1 = new TcpChannel(8085);

ChannelServices.RegisterChannel(chan1);

HttpChannel chan2 = new HttpChannel(8086);

ChannelServices.RegisterChannel(chan2);

When you create a channel, you also specify a type of formatter for the channel. The following sections describe the formatter types available.

Specifying a channel format

At the same time that you create a channel, you also specify a format for the type of channel that you have picked. Two default formatter types are available in the System.Runtime.Remoting.Channels namespace: the TCP channel and the HTTP channel.

System.Runtime.Remoting.Channels.Tcp Namespace

The System.Runtime.Remoting.Channels.Tcp namespace contains channels that use the TCP protocol to transport data between remote objects. The default encoding for the TCP is binary encoding, which makes this an efficient way to pass data between remote objects. Binary data always has a smaller footprint than the equivalent XML data passed through SOAP on an HTTP channel. The downside to using the TCP protocol is that it is a proprietary format, so it only works on systems that understand this formatting type. To make your remote object more accessible, you should use the HTTP channel for encoding, as it will be passing data down the wire in the SOAP protocol. Table 36-3 summarizes the available classes in the System.Runtime.Remoting.Channels.Tcp namespace.

 

Table 36-3: System.Runtime.Remoting.Channels.Tcp Namespace

Class

 

 

Description

TcpChannel

Provides an implementation for a sender-receiver channel that uses the TCP protocol to transmit messages. This class is a combination of the TcpClientChannel class and the TcpServerChannel class, which enables automatic two-way communication over TCP.

TcpClientChannel

 

Provides an implementation for a client channel that

 

 

uses the TCP protocol to transmit messages.

 

 

 

TcpServerChannel

 

Provides an implementation for a server channel that

 

 

uses the TCP protocol to transmit messages.

System.Runtime.Remoting.Channels.Http Namespace

The System.Runtime.Remoting.Channels.Http namespace contains channels that use the HTTP protocol to transport data between remote objects. The default encoding for the HTTP protocol is SOAP, which makes this a flexible way to pass data between remote objects. Table 36-4 summarizes the available classes in the System.Runtime.Remoting.Channels.Http namespace.

 

Table 36-4: System.Runtime.Remoting.Channels.Http Namespace

Class

 

 

Description

HttpChannel

Provides an implementation for a sender-receiver channel that uses the HTTP protocol to transmit messages. This class is a combination of the HttpClientChannel class and the HttpServerChannel class, which enables automatic two-way communication over HTTP.

HttpClientChannel

 

Provides an implementation for a client channel that

 

 

uses the HTTP protocol to transmit messages.

 

 

 

HttpRemotingHandler

 

Implements an ASP.NET handler that forwards

 

 

requests to the remoting HTTP channel.

 

 

 

HttpRemotingHandlerFactory

 

Initializes new instances of the HttpRemotingHandler

Соседние файлы в папке c#