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

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

378 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

try

{

Console.WriteLine("Authenticated user = {0}", proxy2.GetIISUser());

}

catch (WebException)

{

Console.WriteLine("Integrated Windows authentication failed");

}

//Create a proxy that authenticates the user with a client

//certificate loaded from a file.

MyWebService proxy3 = new MyWebService(); proxy3.ConnectionGroupName = "Test3"; X509Certificate cert1 =

X509Certificate.CreateFromCertFile(@"..\..\TestCertificate.cer"); proxy3.ClientCertificates.Add(cert1);

try

{

Console.WriteLine("Authenticated user = {0}", proxy3.GetIISUser());

}

catch (WebException)

{

Console.WriteLine("Certificate authentication failed");

}

// Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete. Press Enter"); Console.ReadLine();

}

}

}

10-15. Call a Web Method Asynchronously

Problem

You need to invoke a Web method on another thread so that your program can continue with other tasks (such as updating the user interface) while waiting for the response.

Solution

Use the proxy class’s built-in asynchronous method and asynchronous completion event, which are automatically generated for every Web method supported by the XML Web service. The method is named XXXAsync, and the completion event is named XXXCompleted, where XXX is the name of the original, synchronous method.

How It Works

The automatically generated proxy class has the features you need to call any Web method asynchronously. For example, consider the Wait Web method shown in the following code, which pauses for a random number of seconds between a lower and an upper value:

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

379

//Returns the specified string after a random delay

//between a lower and upper bound.

[WebMethod()]

public string Echo(string str, int lower, int upper)

{

//Sleep for a random period of time between the specified

//lower and upper boundaries.

Random rand = new Random();

Thread.Sleep(rand.Next(lower,upper));

// Echo back the specified string. return str;

}

The proxy class generated for the Web service that exposes the Echo method will also implement a method named EchoAsync, an event named EchoCompleted, an event argument data class named

EchoCompletedEventArgs, and a delegate named EchoCompletedEventHandler. Together, these program elements allow you to call the Echo web method asynchronously and handle the result—regardless of whether the call fails or succeeds. All Web methods follow the same model; only the names are changed. Each of these elements is described here:

The EchoAsync method takes the same arguments as the Echo method, with the option of providing an additional object argument that can be used for general state information. This extra state is passed to the EchoCompletedEventHandler (described next) when the asynchronous call completes and is often used to match completed events to original calls. When you call EchoAsync, the .NET Framework returns control immediately to the calling code so that it can continue processing but executes the method on a thread from the thread pool.

When the EchoAsync method completes, the proxy raises the EchoCompleted event using a thread from the thread pool. To handle these events, you must add an EchoCompletedEventHandler delegate to the event. The EchoCompletedEventHandler delegate declares two arguments. The first argument is an object that is a reference to the sender (or source) of the event, which is the proxy object. The second argument is an EchoCompletedEventArgs object, which is discussed next.

The EchoCompletedEventArgs class provides access to the result of the asynchronous operation. The Cancelled property indicates whether the operation was canceled by a call to the CancelAsynch method, Error contains any exception that was raised that caused the asynchronous operation to fail, UserState contains the user state object (if any) that was passed to the EchoAsync method, and Result is of the same type returned by Echo and contains the result of the asynchronous call if it succeeded.

Note The asynchronous model described in this recipe is new to the Web service proxy code generated by Visual Studio 2005. In earlier versions of Visual Studio and in the code generated by the Web Services Description Language tool (wsdl.exe), a different asynchronous model is implemented. Instead of an XXXAsync method and the use of events, the proxy would have BeginXXX and EndXXX methods. This old approach had the benefit of providing you with System.Threading.WaitHandle objects for the asynchronous operations, which you could use for multithreaded synchronization.

The Code

The following example demonstrates how to call a Web method named Echo asynchronously using the automatically generated EchoAsync method of the proxy. The EchoAsync method is called three times, and the second instance is canceled before it has a chance to complete. The EchoCompletedHandler method processes the results of the three asynchronous method calls.

380C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

using System;

using System.Threading;

using Recipe10_15.MyWebService;

namespace Apress.VisualCSharpRecipes.Chapter10

{

class Recipe10_15

{

private static void Main()

{

//Create a proxy through which to execute the methods of

//the Web service.

MyWebService proxy = new MyWebService();

//Add an event handler to the EchoCompleted event. proxy.EchoCompleted += EchoCompletedHandler;

//Call Echo three times asynchronously. proxy.EchoAsync("Echo String 1", 7000, 10000, "Test1"); proxy.EchoAsync("Echo String 2", 5000, 10000, "Test2"); proxy.EchoAsync("Echo String 3", 1000, 10000, "Test3");

//Quickly cancel the second asynchronous operation. proxy.CancelAsync("Test2");

//Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete. Press Enter"); Console.ReadLine();

}

// A method to handle asynchronous Echo completion events. private static void EchoCompletedHandler(object sender,

EchoCompletedEventArgs args)

{

if (args.Error != null)

{

Console.WriteLine("{0}: {1}", args.UserState,args.Error.Message);

}

else if (args.Cancelled)

{

Console.WriteLine("{0}: operation cancelled before completion.", args.UserState);

}

else

{

Console.WriteLine("{0}: Succeeded, echoed string = {1}.", args.UserState, args.Result);

}

}

}

}

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

381

10-16. Make an Object Remotable

Problem

You need to create a class that can be accessed from another application or another computer on the network. However, you don’t need cross-platform compatibility, and you want optimum performance.

Solution

Make the class remotable by deriving from System.MarshalByRefObject, and create a component host that registers the class with the .NET Remoting infrastructure.

How It Works

Remoting allows you to make an object accessible across process and machine boundaries. While XML Web services are ideal when you need to share functionality across platforms or trust boundaries, Remoting is the best-performing choice for a closed system in which all components are built on .NET and the Windows operating system. To use .NET Remoting, you need the following ingredients, each of which must reside in a separate assembly:

A remotable object: This object can be accessed from other applications and computers and must derive from the System.MarshalByRefObject.

A component host: This application registers the remotable type with the .NET Remoting infrastructure using the RemotingConfiguration class from the System.Runtime.Remoting namespace. You can use any type of long-running .NET Framework application for a component host (including Windows Forms–based applications, Windows services, console applications, and even IIS). As long as the component host is running, remote clients can create or connect to existing instances of the remotable object. The component host never interacts with the remotable objects directly. All it does is register the appropriate types with the .NET Remoting infrastructure. After this point, clients can create object instances, and the server application can continue with other tasks. However, when the component host is closed, any remotable objects will be destroyed, and no more hosted objects can be created.

A client application: This application can create or connect to instances of the remotable class in the component host process and interact with them. The client uses the RemotingConfiguration class to register the types it wants to access remotely. The client application uses the RemotingConfiguration.Configure method to register the remote objects it wants to call. Once this step is taken, the client can create the object exactly as it would create a local object. However, the object will actually be created in the component host.

Figure 10-3 shows how these three parts interact. This example has only one client. However, it’s also possible for multiple clients to create instances of the remotable class at the same time. In this case, you can configure the Remoting host whether each client has its own remotable object instance or whether all clients share a single instance.

382

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

Figure 10-3. Using a remotable class

Note Ideally, the remote object won’t retain any state. This characteristic allows you to use single-call activation, in which object instances are created at the beginning of each method call and released at the end, much like an XML Web service. This ensures your objects consume the fewest possible server resources and saves you from the added complexity of implementing a lease policy to configure object lifetime.

The Code

The following example demonstrates the declaration of a remotable class that reads data from the Authors table of the pubs database and returns a System.Data.DataTable. Notice that the only Remoting-specific code is the derivation of the class from the System.MarshalByRef class.

using System; using System.Data;

using System.Data.SqlClient;

namespace Apress.VisualCSharpRecipes.Chapter10

{

// Define a class that extends MarshalByRefObject, making it remotable. public class Recipe10_16 : MarshalByRefObject

{

private static string connectionString = @"Data Source=.\SQLEXPRESS;" + "Initial Catalog=PUBS;Integrated Security=SSPI";

//The DataTable returned by this method is serializable, meaning that the

//data will be physically passed back to the caller across the network. public DataTable GetAuthors()

{

string SQL = "SELECT * FROM Authors";

//Create ADO.NET objects to execute the DB query. SqlConnection con = new SqlConnection(connectionString); SqlCommand com = new SqlCommand(SQL, con); SqlDataAdapter adapter = new SqlDataAdapter(com); DataSet ds = new DataSet();

//Execute the command.

try

{

con.Open();

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

383

adapter.Fill(ds, "Authors");

}

catch (Exception err)

{

Console.WriteLine(err.ToString());

}

finally

{

con.Close();

}

// Return the first DataTable in the DataSet to the caller. return ds.Tables[0];

}

// This method allows you to verify that the object is running remotely. public string GetHostLocation()

{

return AppDomain.CurrentDomain.FriendlyName;

}

}

}

Usage

To use the Recipe10_16 class remotely, you must host it and then create a client that uses the remote object. Here is the code for a simple console component host:

using System;

using System.Runtime.Remoting;

namespace Apress.VisualCSharpRecipes.Chapter10

{

class Recipe10_16Host

{

private static void Main()

{

//Register the remotable classes defined in the specified

//configuration file. RemotingConfiguration.Configure("Recipe10-16Host.exe.config");

//As long as this application is running, the registered remote

//objects will be accessible.

Console.Clear();

Console.WriteLine("Press Enter to shut down the host."); Console.ReadLine();

}

}

}

The component host uses the following configuration file (Recipe10-16Host.exe.config) to configure the classes it will support, the ports it will support for network communication, and the URI that the client will use to access the object. This Recipe10-16 assembly containing the implementation of the remote object class must be in the global assembly cache (GAC) or in the same directory as the server application. The configuration file also configures the remote object to use single-call activation, meaning that a new object is created for each client call.

384 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

<?xml version="1.0" encoding="utf-8" ?> <configuration>

<system.runtime.remoting>

<application>

<!-- Define the remotable types. --> <service>

<wellknown

mode = "SingleCall"

type = "Apress.VisualCSharpRecipes.Chapter10.Recipe10_16,

Recipe10-16"

objectUri = "Recipe10-16" /> </service>

<!-- Define the protocol used for network access. You can use tcp or http channels. -->

<channels>

<channel ref="tcp" port="19080" /> </channels>

</application>

</system.runtime.remoting>

</configuration>

The following code shows a simple client that uses the remote object created earlier. Notice that in this example, the configuration of the Remoting infrastructure is performed programmatically instead of using the configuration file. You should avoid such an approach when using static configuration values because using configuration files provides more flexibility. However, if you want to dynamically configure the Remoting infrastructure, you will need to be familiar with the approach demonstrated here. Unfortunately, a complete discussion is beyond the scope of this book; for detailed information, see Advanced .NET Remoting, Second Edition by Ingo Rammer and Mario Szpuszta (Apress, 2004). Note that as with the host, the assembly containing the declaration of the class that will be accessed remotely must still be accessible to the client application, either in the local folder or in the local GAC.

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Data;

namespace Apress.VisualCSharpRecipes.Chapter10

{

class Recipe10_16Client

{

public static void Main()

{

//Register a new TCP Remoting channel to communicate with the

//remote object.

ChannelServices.RegisterChannel(new TcpChannel());

// Register the classes that will be accessed remotely. RemotingConfiguration.RegisterWellKnownClientType(

typeof(Recipe10_16), @"tcp://localhost:19080/Recipe10-16");

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

385

//Now any attempts to instantiate the Recipe10_16

//class will actually create a proxy to a remote instance.

//Interact with the remote object through a proxy. Recipe10_16 proxy = new Recipe10_16();

//Display the name of the component host application domain

//where the object executes.

Console.WriteLine("Object executing in: " + proxy.GetHostLocation());

// Get the DataTable from the remote object and display its contents. DataTable dt = proxy.GetAuthors();

foreach (DataRow row in dt.Rows)

{

Console.WriteLine(row[1]);

}

// Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete. Press Enter"); Console.ReadLine();

}

}

}

10-17. Register All the Remotable Classes

in an Assembly

Problem

You want to register all the remotable classes that are defined in an assembly without having to specify them in a configuration file.

Solution

Load the assembly with the remotable classes using reflection. Loop through all its public types, and use the RemotingConfiguration.RegisterWellKnownServiceType method to register every remotable class.

How It Works

.NET makes it equally easy to register remotable classes through a configuration file or programmatically with code. The type being registered must extend MarshalByRefObject, and then you call

RemotingConfiguration.RegisterWellKnownServiceType, passing on the type, the URI on which remote clients can connect to the type, and a value of the System.Runtime.Remoting.WellKnownObjectMode enumeration, which describes how the Remoting infrastructure should map client calls to object instances. The possible values are SingleCall, in which every incoming call is serviced by a new object, and Singleton, in which every incoming call is serviced by the same object. When using singleton objects, accurate state management and thread synchronization become critical.

386 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

The Code

The following server code searches for remotable classes in an assembly that is specified as a commandline argument. Each class derived from MarshalByRefObject is registered, and then the example displays the channel where the remotable object is available.

using System;

using System.Reflection; using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp;

namespace Apress.VisualCSharpRecipes.Chapter10

{

class Recipe10_17

{

public static void Main(string[] args)

{

//Ensure there is an argument, we is assumed to be a valid

//filename.

if (args.Length != 1) return;

//Register a new TCP Remoting channel to communicate with the

//remote object.

ChannelServices.RegisterChannel(new TcpChannel(19080));

//Get the registered Remoting channel. TcpChannel channel =

(TcpChannel)ChannelServices.RegisteredChannels[0];

//Create an Assembly object representing the assembly

//where remotable classes are defined.

Assembly assembly = Assembly.LoadFrom(args[0]);

// Process all the public types in the specified assembly. foreach (Type type in assembly.GetExportedTypes())

{

// Check if the type is remotable.

if (type.IsSubclassOf(typeof(MarshalByRefObject)))

{

//Register each type using the type name as the URI

//(like ProductsDB). Console.WriteLine("Registering {0}", type.Name); RemotingConfiguration.RegisterWellKnownServiceType(

type,

type.Name,

WellKnownObjectMode.SingleCall);

//Determine the URL where this type is published. string[] urls = channel.GetUrlsForUri(type.Name); Console.WriteLine(urls[0]);

}

}

//As long as this application is running, the registered remote

//objects will be accessible. Console.WriteLine(Environment.NewLine); Console.WriteLine("Press Enter to shut down the host.");

Console.ReadLine();
}
}
}
Note The preceding code determines whether a class is remotable by examining whether it derives from MarshalByRefObject. This approach always works, but it could lead you to expose some types that you don’t want to make remotable. For example, the System.Windows.Forms.Form object derives indirectly from MarshalByRefObject. This means if your remote object library contains any forms, they will be exposed remotely. To avoid this problem, don’t include remotable types in your assembly unless you want to make them publicly available. Or, identify the types you want to register with a custom attribute. You could then check for this attribute before registering a type.
10-18. Host a Remote Object in IIS
Problem
You want to create a remotable object in IIS (perhaps so that you can use SSL or IIS authentication) instead of a dedicated component host.
Solution
Place the configuration file and assembly in a virtual directory, and modify the object URI so that it ends in .rem or .soap.
How It Works
Instead of creating a dedicated component host, you can host a remotable class in IIS. This allows you to ensure that the remotable classes will always be available, and it allows you to use IIS features such as SSL encryption and integrated Windows authentication.
To host a remotable class in IIS, you must first create a virtual directory. The virtual directory will contain two things: a configuration file named Web.config that registers the remotable classes and a Bin directory where you must place the corresponding class library assembly (or install the assembly in the GAC).
The configuration file for hosting in IIS is quite similar to the configuration file you use with a custom component host. However, you must follow several additional rules:

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

387

 

 

 

 

You must use the HTTP channel (although you can use the binary formatter for smaller message sizes).

You cannot specify a specific port number for listening. IIS listens on all the ports you have configured in IIS Manager. Typically, this will be ports 80 and 443 (for secure SSL communication).

The object URI must end with .rem or .soap.

The configuration file must be named Web.config, or it will be ignored.

The Code

Here’s an example Web.config file that registers the remote class shown in recipe 10-16:

Соседние файлы в предмете Программирование на C++