I'm experimenting ideas around using AppDomain to manage some legacy code contains lots of static fields in a multi-threaded environment.
I read answers this question: How to use an AppDomain to limit a static class' scope for thread-safe use?, thought it's quite promising and decided to try it out with a very simple class in assembly ClassLibrary1.dll:
namespace ClassLibrary1
{
public static class Class1
{
private static int Value = 0;
public static void IncrementAndPrint()
{
Console.WriteLine(Value++);
}
}
}
and here's my code that loads the assemblyinto 2 different app domains and invokes the IncrementAndPrint() several times:
var appDomain1 = System.AppDomain.CreateDomain("AppDomain1");
var appDomain2 = System.AppDomain.CreateDomain("AppDomain2");
var assemblyInAppDomain1 = appDomain1.Load("ClassLibrary1");
var assemblyInAppDomain2 = appDomain2.Load("ClassLibrary1");
var class1InAppDomain1 = assemblyInAppDomain1.GetType("ClassLibrary1.Class1");
var class1InAppDomain2 = assemblyInAppDomain2.GetType("ClassLibrary1.Class1");
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
I was expecting the output to be:
0
1
2
0
1
2
because there will be a copy of the static field Value to local to each instance of AppDomain. However, instead what I got was:
0
1
2
3
4
5
which tells me they are still all sharing the same copy of the static field Value. Can anyone tell me what have I done wrong here?
Update:
I tried Erik's suggestion, now I call CreateInstanceAndUnwrap() method of the AppDomain class instead of calling Load() and GetType() as shown below. Also, I've converted IncrementAndPrint to an instance method rather than a static method. However, I'm still getting the same result.
var appDomain1 = System.AppDomain.CreateDomain("AppDomain1");
var appDomain2 = System.AppDomain.CreateDomain("AppDomain2");
var class1InAppDomain1 = (Class1)appDomain1.CreateInstanceAndUnwrap("ClassLibrary1", "ClassLibrary1.Class1");
var class1InAppDomain2 = (Class1)appDomain2.CreateInstanceAndUnwrap("ClassLibrary1", "ClassLibrary1.Class1");
class1InAppDomain1.IncrementAndPrint();
class1InAppDomain1.IncrementAndPrint();
class1InAppDomain1.IncrementAndPrint();
class1InAppDomain2.IncrementAndPrint();
class1InAppDomain2.IncrementAndPrint();
class1InAppDomain2.IncrementAndPrint();
It looks like you are loading a type from another appDomain into the current appDomain. Thus the code that calls the static methods are calling from the current appDomain.
I'm unaware of any other way to call a static method in another domain without creating an instance of an object in another domain, and having that object call the static method.
Example: Solution contains 2 Projects (ClassLibrary and a Winforms/Console app)
[ClassLibrary]
using System;
namespace MyLibrary
{
public class DomainObject : MarshalByRefObject
{
private static int _Value;
private static void IncrementValue()
{
DomainObject._Value++;
}
public static int Value
{
get
{
return DomainObject._Value;
}
}
public int GetIncrementedValue()
{
DomainObject.IncrementValue();
return DomainObject.Value;
}
}
}
[Application]
private void button1_Click(object sender, EventArgs e)
{
AppDomain domain1 = AppDomain.CreateDomain("domain1");
AppDomain domain2 = AppDomain.CreateDomain("domain2");
DomainObject object1 =
domain1.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject")
as DomainObject;
DomainObject object2 =
domain2.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject")
as DomainObject;
if (object1 != null)
{
Console.WriteLine("object 1 Value = "
+ object1.GetIncrementedValue().ToString());
Console.WriteLine("object 1 Value = "
+ object1.GetIncrementedValue().ToString());
Console.WriteLine("object 1 Value = "
+ object1.GetIncrementedValue().ToString());
}
if (object2 != null)
{
Console.WriteLine("object 2 Value = "
+ object2.GetIncrementedValue().ToString());
Console.WriteLine("object 2 Value = "
+ object2.GetIncrementedValue().ToString());
Console.WriteLine("object 2 Value = "
+ object2.GetIncrementedValue().ToString());
}
/* Unload the Domain and re-create
* This should reset the Static Value in the AppDomain
*/
AppDomain.Unload(domain1);
domain1 = AppDomain.CreateDomain("domain1");
object1 = domain1.CreateInstanceAndUnwrap("MyLibrary",
"MyLibrary.DomainObject")
as DomainObject;
if (object1 != null)
{
Console.WriteLine("object 1 Value = "
+ object1.GetIncrementedValue().ToString());
Console.WriteLine("object 1 Value = "
+ object1.GetIncrementedValue().ToString());
Console.WriteLine("object 1 Value = "
+ object1.GetIncrementedValue().ToString());
}
if (object2 != null)
{
Console.WriteLine("object 2 Value = "
+ object2.GetIncrementedValue().ToString());
Console.WriteLine("object 2 Value = "
+ object2.GetIncrementedValue().ToString());
Console.WriteLine("object 2 Value = "
+ object2.GetIncrementedValue().ToString());
}
}
Generated Results:
object 1 Value = 1
object 1 Value = 2
object 1 Value = 3
object 2 Value = 1
object 2 Value = 2
object 2 Value = 3
object 1 Value = 1
object 1 Value = 2
object 1 Value = 3
object 2 Value = 4
object 2 Value = 5
object 2 Value = 6