views:

394

answers:

1

I am having issues while passing objects across Appdomains.During further investigation I found that the issue is due to the IronPython object not been Serialized. This IronPython object is derived from a .NET base class. The .NET base class is derived from MarshalByRefObj.

Let me explain my environment. I have IronPython embedded in my C# application. It is imposed that every class in IronPython inherit the .NET base class say ClassA. ClassA is derived from MarshalByRefObj as I need to pass an instance of this class to another appdomain. I create a new appdomain and pass the instance of ClassA to this Appdomain. While calling a method in python class through the instance of ClassA I get an exception mentioning that "Type 'IronPython.Runtime.Types.PythonType' in Assembly 'IronPython, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' is not marked as serializable"

How can I serialize python objects from C# or is there any other way to tackle this situation.

Update

More insight into the problem. If I instantiate the class where I access python methods in the default appdomain and then pass the instance to the created appdomain then the above mentioned issue is not seen. On the other side when I Instantiate the class where I access the python method in the created appdomain and then access the python methods then the serialization exception is thrown.

One way I see to resolve this issue is that i modify the IronPython source code to serialize the types that are required. Is there any other way that i can do to get around this issue.

Here is a sample code to reproduce the exception I encountered

using System;
using Microsoft.Scripting;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

class Foo
{
    public static void Main(string[] args)
    {
        AppDomain ad = AppDomain.CreateDomain("foo");
        var engine = Python.CreateEngine(ad);
        engine.Runtime.LoadAssembly(typeof(MbrBase).Assembly);

        var code = engine.CreateScriptSourceFromString(@"
import MbrBase
class C(MbrBase):
    pass

a = C()
", SourceCodeKind.Statements);

        var scope = engine.CreateScope();
        code.Execute(scope);

        Console.WriteLine("Trying to do it... {0}",
AppDomain.CurrentDomain.Id);
        MbrBase mbr = (MbrBase)scope.GetVariable("a");

        string isSubClassCode = String.Format("issubclass({0},{1})", "C",
"MbrBase");
        ScriptSource script =
engine.CreateScriptSourceFromString(isSubClassCode,
SourceCodeKind.Expression);
        bool result = (bool)script.Execute(scope);

        if (result == true)
        {
            ObjectOperations ops = engine.Operations;

            object subClass = scope.GetVariable("C");
            object instance = ops.Call(subClass);

            mbr = instance as MbrBase;
        }

        mbr.DoItVirtually();
        mbr.DoIt();
        Console.ReadKey();
    }
}

public class MbrBase : MarshalByRefObject
{
    public virtual void DoItVirtually()
    {
        Console.WriteLine("Did it virtually {0}", AppDomain.CurrentDomain.Id);
    }

    public void DoIt()
    {
        Console.WriteLine("Did it {0}", AppDomain.CurrentDomain.Id);
    }
}
A: 

This works for me now.

The problem was that I was trying to return the objects from the remote domain into the local domain. ObjectOperations has a set of overloads which take ObjectHandles and has some other methods which return ObjectHandles for working with objects in a remote app domain. If the above code is modified to the code below it works.

Add: using System.Runtime.Remoting

        var subClass = scope.GetVariableHandle("C"); // get back a handle
        var instance = ops.Invoke(subClass, new ObjectHandle[0]); // invoke the handle to create an instance

        mbr = instance.Unwrap() as MbrBase; // now we know we have an MBR and we can unwrap it to our local side

P.S. I got the solution from Iron Python community.

MUSTAQ