Static Fields in Appdomain

Static Variable Instances and AppDomains, what is happening?

There are only two ways for a class to be accessible from another AppDomain- one is is the class is [Serializable], as your Test class is, the other is if the class inherits from MarshalByRefObject. Because your class is Serializable, a copy of it is created for every cross-AppDomain call. So the Test that the main appdomain gets when you call...

Test TObj = newDomain.CreateInstanceAndUnwrap(typeof(Test).Assembly.FullName, typeof(Test).FullName) as Test;

is actually not the Test instance that was created in the "DomNew" AppDomain- it is a copy local to the "main" AppDomain, and therefore references the static variables from the "main" AppDomain.

If you want Test to exhibit the behavior that you expect, make it inherit from MarshalByRefObject instead of being Serializable.

Static Fields in AppDomain

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

Get static List from AppDomain

Static variables are limited to the current App domain. If you have N different app domains, then you have N different values for a static property.

In C# Language Specification 5.0 :

10.5.1 A static field is not part of a specific instance; instead, it is shared amongst all instances of a closed type (§4.4.2). No matter how many instances of a closed class type are created, there is only ever one copy of a static field for the associated application domain.

Load static class in appdomain

Method (b) fails because AppDomain.Load cannot resolve assemblies that are not in the base app dir, the probing private paths or the GAC.

Also note that the AppDomain.Load is not loading the assembly on the specific AppDomain (like adapterDomain.Load in your sample code). Instead it is loading it on the current AppDomain (this is the one making the call to AppDomain.Load. This behavior is remarked on the MSDN documentation.) Apparently this is not what you are looking for.

Here's an example on how to call a static method in the child AppDomain:

class Program
{
static void Main(string[] args)
{
// This is for testing purposes!
var loadedAssembliesBefore = AppDomain.CurrentDomain.GetAssemblies();

var domain = AppDomain.CreateDomain("ChildDomain");
// This will make the call to the static method in the dhild AppDomain.
domain.DoCallBack(LoadAssemblyAndCallStaticMethod);
// Print the loaded assemblies on the child AppDomain. This is for testing purposes!
domain.DoCallBack(PrintLoadedAssemblies);
AppDomain.Unload(domain);

// This is for testing purposes!
var loadedAssembliesAfter = AppDomain.CurrentDomain.GetAssemblies();
// Assert that no assembly was leaked to the main AppDomain.
Debug.Assert(!loadedAssembliesBefore.Except(loadedAssembliesAfter).Any());

Console.ReadKey();
}

// Loads StaticMethodInHere.dll to the current AppDomain and calls static method
// StaticClass.DoSomething.
static void LoadAssemblyAndCallStaticMethod()
{
var assembly = Assembly.LoadFrom(@"PATH_TO_ASSEMBLY");

assembly.GetType("CLASS_CONTAINING_STATIC_METHOD")
.InvokeMember("STATIC_METHOD",
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.InvokeMethod,
null,
null,
null);
}

// Prints the loaded assebmlies in the current AppDomain. For testing purposes.
static void PrintLoadedAssemblies()
{
Console.WriteLine("/ Assemblies in {0} -------------------------------",
AppDomain.CurrentDomain.FriendlyName);

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine(assembly.FullName);
}
}
}

To make this work you will need to replace:

  • PATH_TO_ASSEMBLY with the path of the assembly containing the static method including the extension.
  • CLASS_CONTAINING_STATIC_METHOD with the name of the class containing the static method including the namespace of the class.
  • STATIC_METHOD with the name of the static method.

Note that the BindingFlags are set for public static methods.



Related Topics



Leave a reply



Submit