Initialising server activated objects in .net remoting - c#

I am using single-call server activated objects in .net remoting, and have a question about how to initialise the remoted objects.
When my server calls RemotingConfiguration.RegisterWellKnownServiceType() it passes a reference to the Type of the object to be instantiated to service client requests. How do I initialise these 'remote objects' after thay have been created and before the client uses them?
In my case, the remote objects need to connect to a database, so they need a connection string. How should they acquire this when the containing server doesn't known when they are created, and the remote object has no references to the containing server? What am I missing here?
(My workaround for the moment is to store the connection string in a static field, since all remote objects currently use the same database. This isn't very flexible though, and looks like a hack to me.)

I don't think there is an easy and clean way to just create the object. The object gets created when a remoting call comes in.
I don't think there is a need for that either though. You can create another class that handles all the initialization (it gets the connection string from wherever it wants, etc.) and then you can pull the connection string from the remoting object. You would create that object(s) on process start yourself.
Otherwise, you can put some logic in the constructor of the object. It can maybe read the connection string from a config file or whatever you like. You can also lazy load the variables decalred in that object (i.e. if connectionString = null, then GetConnectionString();) There are plenty of options really.

Please keep in mind I would not suggest doing initialization work in a SingleCall SAO as this work has to be done on every call to the SAO.
To manage connection strings I use a connection manager class.
Please consider the following code pseudo code as I just wrote it out to illustrate an idea. I've used this pattern for various systems I've written.
public enum DBConnection
{
DBTest1,
DBTest2
}
public static class ConnectionStringManager
{
static Exception _construtorException = null;
static Dictionary _connectionMap = new Dictionary();
static ConnectionStringManager()
{
try
{
// Load the _connectionMap
// You can use a custom application configuration section or
// connection strings defined as described in the following article
// http://msdn.microsoft.com/en-us/library/bf7sd233.aspx
}
catch(Exception ex)
{
// Any exception thrown in a static constructor needs to be kept track of because static
// constructors are called during type initialization and exceptions thrown
// are not caught by normal application exception handling.
// (At least as of .NET 2.0)
_constructorException = ex;
}
}
public static string GetConnectionString(DBConnection conn)
{
if ( _constructorEx != null )
throw new Exception("Error initializing ConnectionStringManager", _constructorException);
if ( !_connectionMap.ContainsKey(conn) )
throw new Exception(string.Format("Connection {0} not found", Enum.GetName(typeof(DBconnection), (int)conn));
return _connectionMap[conn];
}
}
You database code will use the connection manager class to retrieve connection strings.
Keep in mind that .NET remoting has been replaced by WCF, but some of us still have remoting in our legacy code :-)
I hope this helps!

Related

Pass parameters from a project to a specific class in another project

I just started to learn C# for a school project but I'm stuck on something.
I have a solution with 2 projects (and each project has a class), something like this:
Solution:
Server (project) (...) MyServerClass.cs, Program.cs
App (project) (...) MyAppClass.cs, Program.cs
In my "MyServerClass.cs", I have this:
class MyServerClass
{
...
public void SomeMethod()
{
Process.Start("App.exe", "MyAppClass");
}
}
How can I properly send, for example, an IP address and port? Would something like this work?
class MyServerClass
{
....
public void SomeMethod()
{
string ip = "127.0.0.1";
int port = 8888;
Process.Start("App.exe", "MyAppClass " + ip + " " + port);
}
}
Then in my "MyAppClass.cs", how can I receive that IP address and port?
EDIT:
The objective of this work is to practice processes/threads/sockets. The idea is having a server that receives emails and filter them, to know if they're spam or not. We got to have 4 or 5 filters. The idea was having them as separated projects (ex: Filter1.exe, Filter2.exe, ...), but I was trying to have only 1 project (ex: Filters.exe) and have the filters as classes (Filter1.cs, Filter2.cs, ...), and then create a new process for each different filter.
I guess I'll stick to a project for each filter!
Thanks!
There are a number of ways to achieve this, each with their own pros/cons.
Some possible solutions:
Pass the values in on the command line. Pros: Easy. Cons: Can only be passed in once on launch. Unidirectional (child process can't send info back). Doesn't scale well for complex structured data.
Create a webservice (either in the server or client). Connect to it and either pull/push the appropriate settings. Pros: Flexible, ongoing, potentially bi-directional with some form of polling and works if client/server are on different hosts. Cons: A little bit more complex, requires one app to be able to locate the web address of the other which is trivial locally and more involved over a network.
Use shared memory via a memory mapped file. This approach allows multiple processes to access the same chunk of memory. One process can write the required data and the others can read it. Pros: Efficient, bi-directional, can be disk-backed to persist state through restarts. Cons: Requires pointers and an understanding of how they work. Requires a little more manipulation of data to perform a read/write.
There are dozens more ways. Without knowing your situation in detail, it's hard to recommend one over another.
Edit Re: Updated requirements
Ok, command line is definitely a good choice here. A quick detour into some architecture...
There's no reason you can't do this with a single project.
First up, use an interface to make sure all your filters are interchangeable. Something like this...
public interface IFilter {
FilterResult Filter(string email);
void SetConfig(string config);
}
SetConfig() is optional but potentially useful to reconfigure a filter without a recompile.
You also need to decide what your IFilter's FilterResult is going to be. Is it a pass/fail? Or a score? Maybe some flags and other metrics.
If you wanted to do multiple projects, you'd put that interface in a "shared" or "common" project on its own and reference it from every other project. This also makes it easy for third parties to develop a filter.
Anyway, next up. Let's look at how the filter is hosted. You want something that's going to listen on the network but that's not the responsibility of the filter itself, so we need a network client. What you use here is up to you. WCF in one flavour or another seems to be a prime candidate. Your network client class should take in its constructor a network port to listen on and an instance of the filter...
public class NetworkClient {
private string endpoint;
private IFilter filter;
public NetworkClient(string Endpoint, IFilter Filter) {
this.filter = Filter;
this.endpoint = Endpoint;
this.Setup();
}
void Setup() {
// Set up your network client to listen on endpoint.
// When it receives a message, pass it to filter.Filter(msg);
}
}
Finally, we need an application to host everything. It's up to you whether you go for a console app or winforms/wpf. Depends if you want the process to have a GUI. If it's running as a service, the UI won't be visible on a user desktop anyway.
So, we'll have a process that takes the endpoint for the NetworkClient to listen on, a class name for the filter to use, and (optionally) a configuration string to be passed in to the filter before first use.
So, in your app's Main(), do something like this...
static void Main() {
try {
const string usage = "Usage: Filter.exe Endpoint FilterType [Config]";
var args = Environment.GetCommandLineArgs();
Type filterType;
IFilter filter;
string endpoint;
string config = null;
NetworkClient networkClient;
switch (args.Length) {
case 0:
throw new InvalidOperationException(String.Format("{0}. An endpoint and filter type are required", usage));
case 1:
throw new InvalidOperationException(String.Format("{0}. A filter type is required", usage));
case 2:
// We've been given an endpoint and type
break;
case 3:
// We've been given an endpoint, type and config.
config = args[3];
break;
default:
throw new InvalidOperationException(String.Format("{0}. Max three parameters supported. If your config contains spaces, ensure you are quoting/escaping as required.", usage));
}
endpoint = args[1];
filterType = Type.GetType(args[2]); //Look at the overloads here to control where you're searching
// Now actually create an instance of the filter
filter = (IFilter)Activator.CreateInstance(filterType);
if (config != null) {
// If required, set config
filter.SetConfig(config);
}
// Make a new NetworkClient and tell it where to listen and what to host.
networkClient = new NetworkClient(endpoint, filter);
// In a console, loop here until shutdown is requested, however you've implemented that.
// In winforms, the main UI loop will keep you alive.
} catch (Exception e) {
Console.WriteLine(e.ToString()); // Or display a dialog
}
}
You should then be able to invoke your process like this...
Filter.exe "127.0.0.1:8000" MyNamespace.MyFilterClass
or
Filter.exe "127.0.0.1:8000" MyNamespace.MyFilterClass "dictionary=en-gb;cutoff=0.5"
Of course, you can use a helper class to convert the config string into something your filter can use (like a dictionary).
When the network client gets a FilterResult back from the filter, it can pass the data back to the server / act accordingly.
I'd also suggest a little reading on Dependency Injection / Inversion of control and Unity. It makes a pluggable architecture much, much simpler. Instead of instantiating everything manually and tracking concrete instances, you can just do something like...
container.Resolve<IFilter>(filterType);
And the container will make sure that you get the appropriate instance for your thread/context.
Hope that helps

How to deal with WCF connection failing

Let's imagine I have WCF service and a client that consumes some methods from a given service.
There are tons of posts of how to handle various exceptions during the client and service communication. Only thing which is still confusing me is a following case:
Service:
[ServiceContract]
public interface IService1
{
[OperationContract]
bool ExportData(object data);
}
public class Service1 : IService1
{
public bool ExportData(object data)
{
// Simulate long operation (i.e. inserting data to the DB)
Thread.Sleep(1000000);
return true;
}
}
Client:
class Program
{
static wsService1.Service1Client client1 = new wsService1.Service1Client();
static void Main(string[] args)
{
object data = GetRecordsFromLocalDB();
bool result = client1.ExportData(data);
if (result)
{
DeleteRecordsFromLocalDB();
}
}
}
Client gets some data from local db and sending it to the server. If result is successful, then client is going to remove exported rows from local DB. Now imagine, when data is already sent to the server, suddenly connection failed (i.e. WiFi was disconnected). In this case data is successfully processed on a server side, but client is never know about it. And yes, I can catch connection exception, but still I don't know what should I do with a records in my local DB. I can send this data again later, but I'll get some duplication on a server DB (i.e. duplication is allowed on remote DB), but I don't want to send same data multiple times.
So, my question is how to handle such cases? What is the best practices?
I checked some info about asynchronous operations. But still this is about when I have stable connection.
As a workaround I can store my export operation under some GUID remotelly and check status for this GUID later. Only thing I can't change remote DB. So, please, suggest what would be better in my case?
Here are some points to consider
On server side you can catch all kinds of error (custom class deriving IErrorHandler) and provide specific error to client letting him know about error's reason.
The concept of service is that it is kind of intermediary between client and database so why would client retrieve data and then send it to service?
One way out is to use transaction which assures that if error occurres then no changes are going to be retained.
By the way, If you expect service to throw an exception do not create global service object since it will end up being in faulted state. Create new instance for every single call instead (make use of using statement so as to dispose its instance). Bool return type does not provide extensive information about the error if any takes place. Let it have void return type and wrap in try/catch block which gives a change to learn more about the source and nature of error.

SslStream, disable session caching

The MSDN documentation says
The Framework caches SSL sessions as they are created and attempts to reuse a cached session for a new request, if possible. When attempting to reuse an SSL session, the Framework uses the first element of ClientCertificates (if there is one), or tries to reuse an anonymous sessions if ClientCertificates is empty.
How can I disable this caching?
At the moment I am experiencing a problem with a reconnect to a server (i.e., the first connection works good, but at attempt to reconnect the servers breaks the session). Restarting the application helps (but of course only for the first connection attempt). I assume the problem root is caching.
I've checked the packets with a sniffer, the difference is at just single place only at Client Hello messages:
First connection to the server (successful):
Second connection attempt (no program restart, failed):
The difference seems to be just the session identifier.
P.S. I'd like to avoid using 3rd-party SSL clients. Is there a reasonable solution?
This is a translation of this question from ru.stackoverflow
Caching is handled inside SecureChannel - internal class that wraps SSPI and used by SslStream. I don't see any points inside that you can use to disable session caching for client connections.
You can clear cache between connections using reflection:
var sslAssembly = Assembly.GetAssembly(typeof(SslStream));
var sslSessionCacheClass = sslAssembly.GetType("System.Net.Security.SslSessionsCache");
var cachedCredsInfo = sslSessionCacheClass.GetField("s_CachedCreds", BindingFlags.NonPublic | BindingFlags.Static);
var cachedCreds = (Hashtable)cachedCredsInfo.GetValue(null);
cachedCreds.Clear();
But it's very bad practice. Consider to fix server side.
So I solved this problem a bit differently. I really didn't like the idea of reflecting out this private static method to dump the cache because you don't really know what you're getting into by doing so; you're basically circumventing encapsulation and that could cause unforeseen problems. But really, I was worried about race conditions where I dump the cache and before I send the request, some other thread comes in and establishes a new session so then my first thread inadvertently hijacks that session. Bad news... anyway, here's what I did.
I stopped to think about whether or not there was a way to sort of isolate the process and then an Android co-worker of mine recalled the availability of AppDomains. We both agreed that spinning one up should allow the Tcp/Ssl call to run, isolated from everything else. This would allow the caching logic to remain intact without causing conflicts between SSL sessions.
Basically, I had originally written my SSL client to be internal to a separate library. Then within that library, I had a public service act as a proxy/mediator to that client. In the application layer, I wanted the ability to switch between services (HSM services, in my case) based on the hardware type, so I wrapped that into an adapter and interfaced that to be used with a factory. Ok, so how is that relevant? Well it just made it easier to do this AppDomain thing cleanly, without forcing this behavior any other consumer of the public service (the proxy/mediator I spoke of). You don't have to follow this abstraction, I just like to share good examples of abstraction whenever I find them :)
Now, in the adapter, instead of calling the service directly, I basically create the domain. Here is the ctor:
public VCRklServiceAdapter(
string hostname,
int port,
IHsmLogger logger)
{
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
ClientId = Guid.NewGuid();
_logger = logger;
_hostname = hostname;
_port = port;
// configure the domain
_instanceDomain = AppDomain.CreateDomain(
$"vcrypt_rkl_instance_{ClientId}",
null,
AppDomain.CurrentDomain.SetupInformation);
// using the configured domain, grab a command instance from which we can
// marshall in some data
_rklServiceRuntime = (IRklServiceRuntime)_instanceDomain.CreateInstanceAndUnwrap(
typeof(VCServiceRuntime).Assembly.FullName,
typeof(VCServiceRuntime).FullName);
}
All this does is creates a named domain from which my actual service will run in isolation. Now, most articles that I came across on how to actually execute within the domain sort of over-simplify how it works. The examples typically involve calling myDomain.DoCallback(() => ...); which isn't wrong, but trying to get data in and out of that domain will likely become problematic as serialization will likely stop you dead in your tracks. Simply put, objects that are instantiated outside of DoCallback() are not the same objects when called from inside of DoCallback since they were created outside of this domain (see object marshalling). So you'll likely get all kinds of serialization errors. This isn't a problem if running the entire operation, input and output and all can occur from inside myDomain.DoCallback() but this is problematic if you need to use external parameters and return something across this AppDomain back to the originating domain.
I came across a different pattern here on SO that worked out for me and solved this problem. Look at _rklServiceRuntime = in my sample ctor. What this is doing is actually asking the domain to instantiate an object for you to act as a proxy from that domain. This will allow you to marshall some objects in and out of it. Here is my implemenation of IRklServiceRuntime:
public interface IRklServiceRuntime
{
RklResponse Run(RklRequest request, string hostname, int port, Guid clientId, IHsmLogger logger);
}
public class VCServiceRuntime : MarshalByRefObject, IRklServiceRuntime
{
public RklResponse Run(
RklRequest request,
string hostname,
int port,
Guid clientId,
IHsmLogger logger)
{
Ensure.IsNotNull(request, nameof(request));
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
// these are set here instead of passed in because they are not
// serializable
var clientCert = ApplicationValues.VCClientCertificate;
var clientCerts = new X509Certificate2Collection(clientCert);
using (var client = new VCServiceClient(hostname, port, clientCerts, clientId, logger))
{
var response = client.RetrieveDeviceKeys(request);
return response;
}
}
}
This inherits from MarshallByRefObject which allows it to cross AppDomain boundaries, and has a single method that takes your external parameters and executes your logic from within the domain that instantiated it.
So now back to the service adapter: All the service adapters has to do now is call _rklServiceRuntime.Run(...) and feed in the necessary, serializable parameters. Now, I just create as many instances of the service adapter as I need and they all run in their own domain. This works for me because my SSL calls are small and brief and these requests are made inside of an internal web service where instancing requests like this is very important. Here is the complete adapter:
public class VCRklServiceAdapter : IRklService
{
private readonly string _hostname;
private readonly int _port;
private readonly IHsmLogger _logger;
private readonly AppDomain _instanceDomain;
private readonly IRklServiceRuntime _rklServiceRuntime;
public Guid ClientId { get; }
public VCRklServiceAdapter(
string hostname,
int port,
IHsmLogger logger)
{
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
ClientId = Guid.NewGuid();
_logger = logger;
_hostname = hostname;
_port = port;
// configure the domain
_instanceDomain = AppDomain.CreateDomain(
$"vc_rkl_instance_{ClientId}",
null,
AppDomain.CurrentDomain.SetupInformation);
// using the configured domain, grab a command instance from which we can
// marshall in some data
_rklServiceRuntime = (IRklServiceRuntime)_instanceDomain.CreateInstanceAndUnwrap(
typeof(VCServiceRuntime).Assembly.FullName,
typeof(VCServiceRuntime).FullName);
}
public RklResponse GetKeys(RklRequest rklRequest)
{
Ensure.IsNotNull(rklRequest, nameof(rklRequest));
var response = _rklServiceRuntime.Run(
rklRequest,
_hostname,
_port,
ClientId,
_logger);
return response;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
AppDomain.Unload(_instanceDomain);
}
}
Notice the dispose method. Don't forget to unload the domain. This service implements IRklService which implements IDisposable, so when I use it, it used with a using statement.
This seems a bit contrived, but it's really not and now the logic will be run on it's own domain, in isolation, and thus the caching logic remains intact but non-problematic. Much better than meddling with the SSLSessionCache!
Please forgive any naming inconsistencies as I was sanitizing the actual names quickly after writing the post.. I hope this helps someone!

Why is the Destructor not being called?

I have a very interesting scenario where I would like a class to inform another entity it has been destroyed; however, its not doing what I want it too.
The Problem
The deconstructor, for some reason does not do what its supposed to do.
The Question
Why is the destructor not being invoked and make sure that it does do its necessary clean up.
The Code
So here we have the informer ~
class Connection
{
public const int Port = 50000;// Can be any range between 49152 and 65536
//Teh Constructor
public Boolean Connect()
{
//SetInformation
Information.Id = 545;
using (var WebServ = new ClientSDKSoapClient("ClientSDKSoap"))
{
ContinueConnection.WaitOne();
WebServ.ClientLogin(Information);
}
return true;
}
~Connection()
{
using (var WebServ = new ClientSDKSoapClient("ClientSDKSoap"))
{
WebServ.ClientLogout(Information);
}
}
}
Additional Information
I want the web service to record if the Connection Class is destroyed for any given reason.
When the client is connecting, it works perfectly. The Web Service records every method called from it. If I call ClientLogout explicitly, it will work.
I am aware I can implement IDisposable; however, this object is not intended to be used within the lifetime of one method. In fact, its intended for use for the entire duration of the program and the failure of this object basically results in the failure of the entire project. (Although I suppose main IS a method...)
I need to release a network connection; however, its not in this program, its in another program and unless ClientLogout is called, it won't be released.
My Research
Microsoft says that you should use the deconstructor for the release of unmanaged resources making an explicit reference to network connections. This ones got my quite stumped.
I think you should implement a Dispose pattern for your Connection class, rather than relying on an obscure deconstructor metaphor. This would be the "canonical" way to do it.
public class Connection : IDisposable // <== Inherit from IDisposable interface
{
public const int Port = 50000;// Can be any range between 49152 and 65536
private SomeType webserv; // Use whatever real type is appropriate here.
private Information information = new Information(); // or whatever
// This is a real constructor.
public Connection()
{
//SetInformation
information.Id = 545;
webServ = new ClientSDKSoapClient("ClientSDKSoap"))
webserv.ContinueConnection.WaitOne();
webServ.ClientLogin(information);
}
// Implement IDisposable interface
public void Dispose()
{
webServ.ClientLogout(information);
}
}
And then use it thusly
using (var connection = new Connection())
{
// Use the connection here.
}
The client will be logged out when you leave the using block.
Microsoft says that you should use the deconstructor for the release of unmanaged resources making an explicit reference to network connections. This ones got my quite stumped.
The docs here are misleading. It really just means you need a finalizer somewhere in your object inheritance chain, to ensure that any unmanaged resources are appropriately cleaned up. But you only need this finalizer once for the entire inheritance tree, at the level where the unmanaged resource is first allocated.
As an example, you do not need a destructor or finalizer if you build a class for a data access layer to wrap the SqlConnection type, because the core SqlConnection type already has one. What you should do, though, is implement IDisposable and write code to ensure prompt disposal, so the finalizer on your SqlConnection will be called sooner, rather than later. But if you were to build a whole new database engine that competes with Sql Server, MySql, Oracle, Access, and the like, and were implementing the ADO.Net provider for this new database engine, then would need to write a finalizer for your connection type, because none exists yet.
In this case, ClientSDKSoap type already has a destructor; you do not need to write another.

.NET Remoting: how to access server application objects from remotable object?

I'm writing a windows service application, which will be accessed through .NET Remoting.
The problem is I can't figure out how to access service objects from remotable class.
For example, I've a handler class:
class Service_console_handler
{
public int something_more = 20;
//some code...
TcpChannel serverChannel = new TcpChannel(9090);
ChannelServices.RegisterChannel(serverChannel);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemoteObject), "RemoteObject.rem",
WellKnownObjectMode.Singleton);
//from here on RemoteObject is accessible by clients.
//some more code doing something and preparing the data...
}
And I've a remotable class:
public class RemoteObject : MarshalByRefObject
{
private int something = 10;
public int Get_something()
{
return something;
}
}
Clients can access data in RemoteObect with no problem. But how can I access Service_console_handler object (i.e. to retrieve useful info from something_more)?
Sorry for dumb questions and thanks in advance.
What you want is somehow to access the instance of ServiceConsoleHandler via a RemoteObject instance, which is visible for the client.
For this you need to consider two things: (1) Get control over the object construction of the RemoteObject instance and make it accessible and (2) modify ServiceConsoleHandler so it can be accessed remotely.
(1)
How would you construct a RemoteObject instance in ServiceConsoleHandler, if you don’t need to consider remoting?
I guess you would do something like this:
class ServiceConsoleHandler
{
…
RemoteObject remoteObject = new RemoteObject();
// now assume that you also already have
// modified the RemoteObject class so it can hold
// a reference to your server:
remoteObject.Server = this;
…
}
It would be nice if you could make this object accessible for the client. You can do this by using RemotingServices.Marshal instead of RemotingConfiguration.RegisterWellKnownServiceType:
class ServiceConsoleHandler
{
…
TcpServerChannel channel = new TcpServerChannel(9090);
ChannelServices.RegisterChannel(channel, true);
RemoteObject remoteObject = new RemoteObject();
remoteObject.Server = this;
RemotingServices.Marshal(remoteObject, "RemoteObject.rem");
…
}
(2)
If you execute the code right now and access the remoteObject.Server in the client code you would get some remoting exception, because the class ServiceConsoleHandler cannot be accessed remotely. Therefore you need the add the [Serializable] attribute:
[Serializable]
class ServiceConsoleHandler
{ … }
Reason: Types which should be accessed remotely, need to be marshaled to some special transferrable representation. This way they can be squeezed through the TCP port and transferred via the TCP protocol. Basic data types can by marshaled by the framework, so you don't need to think about them. For custom types you will need to state, how to do this. One way to do this is by subclassing from MarshalByRefObject. That’s exactly what you have already done with RemoteObject. Another way is to mark your custom classes as [Serializable] as shown above.
That’s it. Now you should be able to access the server’s field in the client code. Note that you don’t need your existing code for object activation:
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, true);
RemoteObject remoteObject = (RemoteObject)Activator.GetObject(
typeof(RemoteObject), "tcp://localhost:9090/RemoteObject.rem");
Console.WriteLine(remoteObject.Server.SomethingMore);
For me .NET remoting is full of funny surprises and sleepless nights. To counter this, make yourself familiar with the remoting concepts (which are from my point of view poorly documented). Dig into the serialization concept (MarshalByRefObject vs. [Serializable]). If you want to make a production code out of it, think a very good ways to handle remoting exceptions. Also consider multithreading. There could be more than one client using this remote object at once.
Have fun!
Thank you! I very much appreciate thoroughness and clarity of you answer.
Most bizzare thing is that I didn't even know that you can publish object instance. About a dozen simple tutorials I studied proposed RemotingConfiguration.RegisterWellKnownServiceType as only method to do remoting. Stupid me.
Now remoting looks much more useful to me. I just wrote a quick test application and it worked. Thanks again.

Categories

Resources