Introduction
How do I instance a CAO object without shipping the source object? This is a question I hear often when people begin to work with CAO objects. I've heard this question often enough that I decided to put together an article describing step by step (so you can follow along with your own implementation) one of the better methods of solving this problem. This method is commonly referred to as the Factory Pattern for CAO Creation.
如何在不发布源代码的情况下,开发客户端激活对象(CAO)。 由于这个问题经常被提出,我决定写一篇文章来做详细介绍。我采用类工厂的模式来实现。
Remoting
Remoting is one of the great innovations of the new .NET framework. It allows for extremely powerful and easy to set up component linking over local or wide area networks. Things that could take days to write and debug in DCOM, COM, ATL, MFC take minutes with .net and remoting.
Remoting 是一个革命性的进步。回想一下COM时代的远程对象实现,噩梦已经远去。
Remoting has several flavors depending on what you need. For this particular article I will be focusing on Client Activated Objects. Client Activated Objects are stateful objects that reside on the server with a proxy on the client. There are a few principal ways to instance a Client Activated Object, however I will be covering the most desirable of these which is the Factory Pattern for CAO's. The Factory Pattern is an MS recommended way of creating CAO objects without needing to ship the fully implemented object.
I've become familiar with CAO's due to some requirements at work for objects that take a very long time to authenticate against a very old legacy system. Re-authentication on every call wouldn't be practical so I created CAO objects to hold the authenticated state. (Doing this securely requires a bit more trickery than I have time to explain in this article, but I will include them in a future article dealing with remoting objects). I also used CAO objects heavily when I created the GCS Version Control System which used CAO objects to deal with multi-call sessions that would otherwise require large amounts of data to be resent on every connection with Singlecall objects. I am trying to redesign that system though to use Singlecall objects.
Despite how useful CAO objects can be I would be remiss if I didn't state that you should avoid using CAO objects if possible. SAO Singlecall objects are a much better choice for most applications and offer much more flexibility and scalability. Before you embark on your CAO object you should first carefully evaluate your project and see if there isn't a way to do what you need efficiently with an SAO Singlecall object first.
Background : Common Remoting Types
Server Activated Objects : Stateless - Singlecall
This object type is the most prevalent and is recommended by Microsoft for use as the primary remoting type. Server activated Singlecall objects hold no state, which makes them ideal for use with clustered web servers, object pooling, and all other sorts of useful things. Since there is no state held in the object, you have to pass all the data that the object needs to do its work on every call.
Server Activated Objects : Stateful - Singleton
These objects are used when you need both a stateful object AND you need that objects data to persist over time and multiple instantiation. If 3 computers instance an SAO Singleton on a server, they will all get a reference to that same object. Singletons are commonly used to direct access to scarce resources and should generally be avoided when possible.
Client Activated Objects : Stateful
CAO Objects are used when you need an object to be stateful throughout its lifetime to its caller. CAO Objects persist data to their caller, however they are different from SAO Singletons in that multiple instantiations of a CAO will get brand new instances of that CAO each time.
The problem : CAO Creation without distributing CAO Source
There are 3 primary ways of creating a CAO Object that I know of. Creating a CAO requires either a copy of the CAO class on the client, a cumbersome class def created through soapsuds, or the presence of an SAO class factory.
| Pros | Cons |
CAO Class visible to client | The simplest method of CAO class creation. | You must ship your entire CAO implementation with the client. Almost always an undesirable situation. |
Soapsuds class def creation | Allows you to keep the implementation for CAO class hidden on server. | Extremely cumbersome. Every time you change anything you must regenerate new class def. |
SAO CAO Class Factory | Allows you to keep the implementation for CAO class hidden on server. Only requires a share dll to allow instantiation on client. Once setup, is extremely flexible to change and update. | A little more complicated than other methods. |
SAO CAO Class Factory
SAO Class Factory creation of CAO objects involves instantiation of an SAO Object (usually Singlecall, although for more complicated tracking reasons some people choose Singletons) which then is used to create a CAO object on the server and pass a reference back to the client. This fairly straightforward sequence produces a CAO object reference on the client that requires none of the CAO class code to reside on the client.
For this example I am going to create a very simple CAO from an equally simple SAO Singlecall class factory. To start with we first need to create a dll we will call 'sharedll.dll'. Also create two console applications called Client and Server. Add these all to the same solution so we can compile and examine the files easily.
After we are done our relevant class and exe breakdown will be as follows.
Step by Step
Now that I've explained the basics, lets get down to the code. I am going to walk you through the steps of going from 0 to a working CAO from an SAO class factory.
Step 1: Setting up definitions
Create a solution and add a class library (I called mine ShareDLL), a console app called Client and a console app called Server. Build the solution, then go to the ref section of Client and Server and add a reference to the ShareDLL to both. Also go into the class files for the client and server and add the using ShareDLL (assuming you used this name for your namespace on the ShareDLL). This will allow both the Client and Server to see the class definitions we will store in the ShareDLL.
Add two abstract classes "SAOCAOClassFactoryDef" and "CAOClassDef" to the class library and inherit them from MarshalByRefObject. These two classes will serve as the basis for the common types that both the client and server see. The classes are marked abstract because we will only ever use them as a definition that points to the actual implementation which will be in the server.
Go to the server and add two corresponding classes "SAOCAOClassFactory" and "CAOClass" that inherit from their corresponding Def parents. These will serve as the implementation classes where the actual work goes on.
// SAOCAOClassFactoryDef.cs in project ShareDLL public abstract class SAOCAOClassFactoryDef : MarshalByRefObject { public abstract CAOClassDef CreateCAOClass(); } // SAOCAOClassFactory.cs in project Server public class SAOCAOClassFactory : ShareDLL.SAOCAOClassFactoryDef {} // CAOClassDef.cs in project ShareDLL public abstract class CAOClassDef : MarshalByRefObject { public abstract int IncrementCounter( int IncSize ); } // CAOClass.cs in project Server public class CAOClass : ShareDLL.CAOClassDef {}
Step 2: Fill in functionality of SAO and CAO
Now that we have our classes defined, lets fill in the functionality. We first need to add a function to the SAO object that creates the CAO object. We do this by adding a function CreateCAOObject to the SAO object that instances the object on the server. Remember to add the abstract definition for the function to the sharedll.
Lets also add some functionality to the object so we can test what makes a CAO unique. I added a variable _Counter in the CAO to show that it doesn't lose state between calls. And I added a method (IncrementCounter) that returns the current value of the counter.
// SAOCAOClassFactory.cs in project Server public class SAOCAOClassFactory : ShareDLL.SAOCAOClassFactoryDef { public override CAOClassDef CreateCAOClass() { return new CAOClass(); // class factory create } } // CAOClass.cs in project Server public class CAOClass : ShareDLL.CAOClassDef { protected int _Counter = 0; // server side persistent variable public override int IncrementCounter( int IncSize ) { _Counter += IncSize; return _Counter; } }
Step 3: Serve your objects
Now our objects are ready to be used so lets serve them. First we create a channel (I chose port 8675). After that you need to register the object with the remoting configuration. This will start the object listening.
Remember, we are not serving the actual CAO object. We are serving a Singlecall version of the SAO object that we will use to instance our CAO object. This is an important distinction so don't be spooked by seeing all the SAO types with no reference to our CAO type.
[Code Based Option] ------------------------------------------- // server.cs in project server ChannelServices.RegisterChannel( new TcpChannel(8675) ); Type SAOType = Type.GetType("Server.SAOCAOClassFactory,Server"); RemotingConfiguration.RegisterWellKnownServiceType( SAOType, "SAOCAOClassFactoryURI", WellKnownObjectMode.SingleCall ); [Config File Option] ------------------------------------------- // Server.exe.config // server.cs in project server RemotingConfiguration.Configure( @"../../Server.exe.config" ); // change this when you go into production
Step 4: Instantiate your objects
Now that we have the object available lets see about instancing it on the client. Instancing the CAO object is extremely simple compared to the server because we merely need to call a method on the SAO object to get an instance of a CAO object.
Note: I don't provide a config file option for the client because with only abstract objects visible to the client (by design) there isn't any way to instantiate them with new. There are ways around this but they are a bit kludgy and get away from the spirit of this article.
// client.cs in project client string url = "tcp://localhost:8675/SAOCAOClassFactoryURI"; Console.WriteLine("Creating SAO."); SAOCAOClassFactoryDef cf = (ShareDLL.SAOCAOClassFactoryDef)Activator.GetObject( typeof(ShareDLL.SAOCAOClassFactoryDef), url ); Console.WriteLine("Creating CAO from SAO Class Factory!"); CAOClassDef mycao = cf.CreateCAOClass(); Console.WriteLine("CAO Created Successfully."); // output is 2 Console.WriteLine( "First call " + mycao.IncrementCounter( 2 ).ToString() ); // output is 5 (proof that object held previous state) Console.WriteLine( "Second call " + mycao.IncrementCounter( 3 ).ToString() );
And that is all you need! You now have a working CAO object taken from an SAO class factory. Run the server first, then run the client, the output should look as seen below. Good luck!
Remarks
In trying to keep this article on point I have kept things as simple and concise as possible. I am writing another article that drills deeply into some of the more interesting aspects of remoting objects in general including lifetime management, security tricks, callbacks, etc that I left out of this one.
I've tested all the code several times to make sure I didn't includey any errors, however if you do find any mistakes or have any questions, feel free to email me allen@glacialcomponents.com. Lastly, I considered adding vb examples as well as c# but I didn't know if there would be any demand for it. If enough people feel it would be helpful I'll add them.
Version | Date | Change | Download | Buy |
1.1 | 10/09/2003 | Updated Examples, updated references and text. | remotingexample.zip |