only for RuBoard |
Now that you're more comfortable with exposing and consuming .NET web services, it's time to discuss the more advanced topic of the .NET Remoting Framework and how you can expose web services by using this Framework.
Microsoft .NET Remoting provides a framework for objects that reside in different application domains, in different processes, and in different machines to seamlessly communicate with each other. .NET Remoting offers a powerful yet simple programming model and run-time support for making these interactions transparent. .NET Channel Services provides the underlying transport mechanism for this communication. The .NET Framework provides the HTTP and TCP channels. The HTTP channel uses SOAP by default to communicate, whereas the TCP channel uses binary payload by default. Different payload-protocol combinations are possible, but this section only discusses the SOAP/XML-HTTP payload-protocol combination.
Two main kinds of remotable objects exist:
Marshal- by-value ( MBV ) objects ” MBV objects are copied and passed out of the application domain. These objects declare their serialization rules (either by implementing ISerializable to implement their own serialization, or by being decorated with SerializableAttribute , which tells the system to serialize the object automatically), but do not extend MarshalByRefObject . The AddressEntry object that we shall be looking at in this section's example is a MBV object.
Marshal-by-reference ( MBR ) objects ” In MBR objects, a proxy is created and used by the client to access the object remotely. The AddressEntries object in the sample is a MBR object. MBR objects extend at least System.MarshalByRefObject .
Two types of activation for MBR objects exist:
Server-activated objects ” These objects are created by the server only when they are needed and not when the client proxy is created by calling new or Activator.GetObject() , but when the client invokes the first method on that proxy . These can be declared as Singleton or SingleCall objects. Singleton objects are objects for which there will always be only one instance, regardless of how many clients exist for that object, and which have a default lifetime. When an object is declared a SingleCall object, the system creates a new object for each client method invocation.
Client-activated objects ” These objects' lifetimes are controlled by the calling application domain, just as they would be if the objects were local to the client. These are created on the server when the client calls a new or Activator.CreateInstance() .
Creating a Remoting Server Object is as simple as creating any other object. The only difference is that an MBR object extends System.MarshalByRefObject and the MBV object is serializable.
Listing 11.14 shows the AddressEntries Remotable Object.
using System; namespace RemotingAddressBookService { //The AddressEntries class represents a Remote Object public class AddressEntries : MarshalByRefObject { private int count = 10 ; public AddressEntry GetAddressEntry(int addrEntryID) { AddressEntry addrEntry = new AddressEntry(addrEntryID); addrEntry.FirstName = "Maria"; addrEntry.LastName = "Anders"; addrEntry.Email = "maria@alfreds-futterkiste.com"; addrEntry.Phone = "030-0074321"; return addrEntry; } public int GetCount() { return count; } } }
Listing 11.15 shows the AddressEntry Remotable Object, which is an MBV object.
using System; namespace RemotingAddressBookService { //The AddressEntry class represents a local object //that can be serialized to the client [SerializableAttribute()] public class AddressEntry { private int id; private string firstName ; private string lastName ; private string email ; private string phone ; public AddressEntry(int addrEntryId) { id = addrEntryId ; } public string FirstName { get { return firstName; } set { firstName = value; } } public string LastName { get { return lastName; } set { lastName = value; } } public string Email { get { return email; } set { email = value; } } public string Phone { get { return phone; } set { phone = value; } } } }
The configuration file Remoting.config in Listing 11.15 specifies the Remoting-specific information for the AddressEntries Remote object.
<configuration> <system.runtime.remoting> <application name="RemotingAddressBookService"> <service> <wellknown mode="SingleCall" type="RemotingAddressBookService.AddressEntries,RemoteObject" objectUri="AddressEntries.soap" /> </service> <channels> <channel port="8085" ref="http" /> </channels> </application> </system.runtime.remoting> </configuration>
You can now create a managed executable to register the channels and start listening to the client requests at port 8085, as specified in the configuration file in Listing 11.16. Listing 11.17 shows the code for this listener.
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; namespace RemotingAddressBookService { public class Listener { public static int Main(string [] args) { try { RemotingConfiguration.Configure("Remoting.config"); //the Following commented code shows the programmatic approach which is //an alternative to using a configuration file /* HttpChannel chan = new HttpChannel(8085); ChannelServices.RegisterChannel(chan); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("RemotingAddressBookService.AddressEntries,RemoteObject"), "AddressEntries.soap", WellKnownObjectMode.SingleCall); */ System.Console.WriteLine("Address Book Remoting Service is waiting " + "for requests."); } catch(Exception e) { System.Console.WriteLine(e.Message); }NET;Remoting objects;exposing> System.Console.WriteLine("Press <Enter> to stop this Service..."); System.Console.ReadLine(); return 0; } } }
After you compile and run the executable, the service is running and listening to client requests, as shown in Figure 11.19.
Note
Instead of running an executable on the server machine, it is also possible to create a Windows Service. Creating a Windows Service is relatively simple in the .NET Framework. But because it is outside the scope of this book, it isn't discussed in this chapter. Another available option is to host the service in the Internet Information Server (IIS).
Now that you have the Remoting service running on the server, you must now look at creating an ASP.NET client to consume this service.
To compile a client that accesses the Remoting Server Object, we require the assembly reference for the Remote Object. This can be obtained if the client developer is provided with the Remote Object's assembly. The Remote Server Object developer can create an assembly, which defines the interface for the Remote object, and distribute this information to clients. Alternatively, the client can generate a proxy object by using the Soapsuds command-line utility and a WSDL file. This section looks at using the Soapsuds command-line utilityh and WSDL file. Note that a non-.NET consumer of the Remoting service can use a similar technique because this method doesn't depend on a .NET assembly.
The Soapsuds tool requires a WSDL file to generate the proxy object. The configuration file for the service specifies the HTTP channel running on port 8085 and the objectUri parameter as AddressEntries.soap . You can get a service description file by using the following URL:
http://localhost:8085/AddressEntries.soap?WSDL
You can check the generated WSDL by entering this URL in the address location of the browser. Figure 11.20 shows the generated WSDL file in a browser.
After you have a WSDL file, you can create a proxy for the service by using the WSDL tool or by adding a web reference in a Visual Studio .NET project. Here, we use the Soapsuds tool to create the proxy . The following command creates the source file and the assembly for the proxy , which we can add to our client application:
Soapsuds url:http://localhost:8085/AddressEntries.soap?WSDL - oa:RemoteObject.dll
For more information on all the available options for the Soapsuds tool, refer to Chapter 4.
Listing 11.18 shows the source generated by the Soapsuds tool for the proxy object.
namespace RemotingAddressBookService { using System; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Metadata; using System.Runtime.Remoting.Metadata.W3cXsd2001; [Serializable, SoapType(XmlNamespace="http://schemas.microsoft.com/clr/nsassem/ RemotingAddressBookService/RemoteObject", XmlTypeNamespace="http://schemas.microsoft.com/ clr/nsassem/RemotingAddressBookService/RemoteObject")] public class AddressEntry { // Class Fields public Int32 id; public String firstName; NET;Remoting objects;consuming> public String lastName; public String email; public String phone; } [SoapType(XmlNamespace="http://schemas.microsoft.com/clr/nsassem/Remoting AddressBookService/RemoteObject",XmlTypeNamespace="http:// schemas.microsoft.com/clr/ nsassem/RemotingAddressBookService/ RemoteObject")] public class AddressEntries : System.Runtime.Remoting.Services.RemotingClientProxy { // Constructor public AddressEntries() { base.ConfigureProxy(this.GetType(), "http://165.193.123.15:8085/AddressEntries.soap"); System.Runtime.Remoting.SoapServices.PreLoad(typeof(RemotingAddressBook Service.AddressEntry)); } // Class Methods [SoapMethod(SoapAction="http://schemas.microsoft.com/clr/nsassem/Remoting AddressBookService.AddressEntries/RemoteObject#GetAddressEntry")] public AddressEntry GetAddressEntry(Int32 addrEntryID) { return ((AddressEntries) _tp).GetAddressEntry(addrEntryID); } [SoapMethod(SoapAction="http://schemas.microsoft.com/clr/nsassem/Remoting AddressBookService.AddressEntries/RemoteObject#GetCount")] public Int32 GetCount() { return ((AddressEntries) _tp).GetCount(); } } }
Listing 11.19 shows the part of the code from an ASP.NET page that uses the Remoting Server Object.
private void Page_Load(object sender, System.EventArgs e) { try { HttpChannel channel = null; //Confirm that the channel is not alredy registered if (ChannelServices.GetChannel("AddrHttpChannel")==null) { IDictionary props = new Hashtable(); props["name"] = "AddrHttpChannel"; channel = new HttpChannel(props, null, new BinaryServerFormatterSinkProvider()); //Register the channel ChannelServices.RegisterChannel(channel); } //Get the ObjRef for the AddressEntries Remote Object AddressEntries addrEntires = (AddressEntries)Activator.GetObject(typeof(RemotingAddressBookService.AddressEntries) , "http://localhost:8085/AddressEntries.soap"); if (addrEntires == null) { divAddrCount.InnerHtml = "<B>Could not locate server</B>"; } else { //Call the method GetCount() on the AddressEntries Object divAddrCount.InnerHtml = "<B>Total number of Addresses in the" + " Address Book: </B>" + addrEntires.GetCount(); //Call the method GetAddressEntry() on the AddressEntries Object AddressEntry addrEntry = addrEntires.GetAddressEntry(1); //Use the properties of the serialized AddressEntry Object divAddressEntry.InnerHtml = "<B>Details of Address Entry [id =1]:</B>" + "<BR><B>First Name :</B>" + addrEntry.firstName + "<BR><B>Last Name :</B>" + addrEntry.lastName ; } } catch(RemotingException remEx) { //Handle Remoting Exception divAddrCount.InnerHtml = "<B>The following Remoting Exception occurred:</B><BR>" + remEx.Message ; } catch(System.Net.WebException webEx) { //Handle Web Exception divAddrCount.InnerHtml = "<B>The following Web Exception occurred:</B><BR>" + webEx.Message + " Confirm that the Remoting service is started and try again."; } catch(Exception ex) { //Handle Web Exception divAddrCount.InnerHtml = "<B>The following Exception occurred :</B><BR>" + ex.Message ; } }
Figure 11.21 shows the result of invoking the Client.aspx page.
only for RuBoard |