Saturday, January 22, 2011

WPF - Messaging using .net Remoting

Objects in different processes and application domains can not communicate directly. .net Remoting is a technology to provide this for such communication. Before WCF, this has been the only technology available in .net for inter-process and inter-domain messaging. After WCF, it is not recommended for inter-process communication.

From MSDN:
This topic is specific to a legacy technology that is retained for backward compatibility with existing applications and is not recommended for new development. Distributed applications should now be developed using the Windows Communication Foundation (WCF).

Since now we have this idea that it is a legacy technology for inter-process communication, we would not be discussing it for this purpose. We would just be focusing on inter-domain and intra-process messaging using .net Remoting.

Creating Proxies:
Based on the above discussion we have some idea about what .net remoting is all about. We have this idea that in order to pass message to the remote object we need to use a proxy. So the main question is how to create a proxy. It is very easy to create proxy for an instance in another application domain. We just need to use CreateInstanceAndUnWrap method on the other application domain specifying the name of type and the assembly containing the type.

AppDomain has a variety of CreateInstance methods. They are as follows:

1. CreateInstance (4 overloads)
2. CreateInstanceAndUnwrap (4 overloads)
3. CreateInstanceFrom (4 overloads)
4. CreateInstanceFromAndUnwrap

As you can see that each of the above four methods has 4 different overloads supporting the the very details of the required instantiation. It is difficult to decide which of these methods to use to instantiate. But the most important thing is that each of these methods would create an instance in the other application domain and return a proxy of the object so that it could be used by the requester. It must be remembered that we can not create proxies unless they are fit for remote instantiation. Only those types which inherit from MarshalByRefObject. Otherwise if we attempt to instantiate a type, it results in SerializationException.



This resulted while creating an instance of Student type.

public class Student
{
public string studentName { get; set; }

public void showStudentName()
{
MessageBox.Show(studentName);
}
}

If we just change our type a bit by inheriting from MarshalByRefObject, everything becomes fine.

public class Student : MarshalByRefObject
{
public string studentName { get; set; }

public void showStudentName()
{
MessageBox.Show(studentName);
}
}

MSDN says the following about MarshalByRefObject:

MarshalByRefObject is the base class for objects that communicate across application domain boundaries by exchanging messages using a proxy. Objects that do not inherit from MarshalByRefObject are implicitly marshal by value. When a remote application references a marshal by value object, a copy of the object is passed across application domain boundaries.

Garbage Collection for Proxies:
It should be good to learn that the remote object in other domain is destroyed if there is no use for five minutes. This is called the Lease-Based mechanism. This is provided by CLR automatically for remotely created objects. We can override this behavior by overriding InitializeLifetimeService method of MarshalByRefObject.

Though the return type of this method is System.Object This should return an an object implementing ILease.You can read more about this here:

http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject.initializelifetimeservice.aspx

Remoting Vs Copying:
With remoting, the object is present in other application domain. All the operation on the object are actually executed on this remote domain. The other option is cloning. We can achieve this if the object is serializable either by implementing ISerializable or with [Serializable] attribute.

Let us update the definition of Course as follows:

[Serializable]
public class Course //: MarshalByRefObject
{
public int CourseId { get; set; }
public string CourseName { get; set; }

public override string ToString()
{
return string.Format("AppDomainName: {0}\n Id: {1}, Name: {2}",
AppDomain.CurrentDomain.FriendlyName , CourseId.ToString(), CourseName);
}
}

Here we have updated the Course by commenting out the inheritance from MarshalByRefObject and specifying the [Serializable] attribute. Let us also update the definition of setCourseMethod as follows:

public void setCourse(Course course)
{
this.studentCourse = course;
studentCourse.CourseId = 2;
studentCourse.CourseName = "Updated Course";
}

We are just updating the Id and name of the course. Now we use this as follows:

private void button1_Click(object sender, RoutedEventArgs e)
{
AppDomain appDomain = AppDomain.CreateDomain("MyAppDomain");

Student student = (Student)appDomain.CreateInstanceAndUnwrap(
"OtherAsembly",
typeof(Student).FullName);

student.studentName = "Muhammad";

Course course = new Course() { CourseId = 1, CourseName = "Fault Diagnosis & Reliable Systems Design" };

//This would create a copy for student object in MyAppDomain keeping the other object in the current domain
//student.studentCourse = course;
student.setCourse(course);

//student object proxy. This would be using MyAppDomain's copy of Course Object
MessageBox.Show(string.Format("From remote domain:\n{0}", student.ToString()));

//local course object
MessageBox.Show(string.Format("From Calling domain:\n {0}", course.ToString()));
}

As it is apparent from the above code, this would result in two message boxes, one after the other. setCourse serializes the course object and passes it to the other application domain where a new copy of Course is instantiated. Based on this discussion the updates in setCourse() method applied to the Course object should not be available in the original copy. Let's see the message boxes displayed.

First Message Box:


Second Message Box:



Now it is logical to think what happens when a type is both serializable and it also inherits MarshalByRefObject. In this case MarshalByRefObject's effect takes over and a remoting takes place instead of cloning. Let's update Course as follows:
[Serializable]
public class Course : MarshalByRefObject
{...}

Now execute the same code and see the message boxes displayed.

First Message Box:



Second Message Box:



Proxies generating other proxies:
It is also possible for proxies to generate other proxies. They can be generated when a method is called on the proxy and it returns some other instance. They can also be generated when we get some property of the proxy which is also an instance type. All such instances have the same requirements as we have discussed above are still applicable i.e. inheriting MarshalByRefObject for remoting or being serializable for cloning.

Let's define a new class called Degree as follows:

public class Degree
{
public string DegreeName { get; set; }
}

We use this for our Student as follows:

public class Student : MarshalByRefObject
{
public string studentName { get; set; }
public Course studentCourse { get; set; }

public Student()
{
this.studentDegree = new Degree();
}

//public override string ToString()
//{
// return string.Format("Student: AppDomainName: {0}, {1} \n\nCourse: {2}",
// AppDomain.CurrentDomain.FriendlyName,
// studentName, studentCourse.ToString());
//}

public override string ToString()
{
return string.Format("Student: AppDomainName: {0}, {1} \n\nCourse: {2}",
AppDomain.CurrentDomain.FriendlyName,
studentName, studentDegree.ToString());
}

public Degree studentDegree { get; set; }

public void setCourse(Course course)
{
this.studentCourse = course;
studentCourse.CourseId = 2;
studentCourse.CourseName = "Updated Course";
}
}

In the above code we have introduced a new property studentDegree. We are instantiating it in the constructor. We have also updated the definition of ToString() to verify our discussion.

Let's use this in our form as follows:

private void btnDegree_Click(object sender, RoutedEventArgs e)
{
AppDomain appDomain = AppDomain.CreateDomain("MyAppDomain");

Student student = (Student)appDomain.CreateInstanceAndUnwrap(
"OtherAssembly",
typeof(Student).FullName);

student.studentName = "Muhammad";

Degree degree = student.studentDegree;
MessageBox.Show(student.ToString());
}

As Degree does not inherit from MarshalByRefObject, it results in the following:



Now let us we update the definition of Degree and inherit it from MarshalByRefObject as follows:

public class Degree : MarshalByRefObject
{
public string DegreeName { get; set; }
}

It results in the following message box:



Reverse Remoting:
First of , this is not a standard term but I thought it is a good name for this discussion. The idea is to discuss what happens behind the scene when we pass object references to a proxy either through method invocation or property assignment.

Method Invocation on Student Object's proxy:

Course course = new Course();
student.setCourse(course);

Reference assignment to Proxy's properties:

Course course = new Course();
student.studentCourse = course;

In both cases below, the runtime would create a proxy for Course object in the current domain and pass it to the Student object which is instantiated in the other domain. Let's see what this has been resulted in by showing the string representation of the student.
MessageBox.Show(student.ToString());

Since we have overloaded ToString() for Student. We see the following MessageBox.



It must be remembered that for both of these scenarios, the Course has to be inheriting MarshalByRefObject. Otherwise, it can be serializable if all we need is cloning the object rather than passing the reference. If Course wasn't inheriting from MarshalByRefObject, this would have resulted in exception like this:



Note:
Java has a similar technology called RMI. RMI stands for Remote Method Invocation. This works on the same idea of .net remoting.

Download:

No comments: