2

I want to get access to a static List of Class2, which will be created in Class1.

Class1 is loaded in an AppDomain and loads Class2 in an other AppDomain. But if I want to get access to the static List in Class2, I get two different Lists.

I think I have to access to the same AppDomain in Class1 to get Class2, but how I can achieve this, if the objects of Class1 are in different AppDomains?

By the way: It is not neccessary to put Class2 in an other AppDomain, but I thought I could get the same object if I would do it.

Here is my code:

public class Class1 : MarshalByRefObject
{
    Class2 class2;

    public Class2 Class2
    {
        get { return class2; }
        set { class2 = value; }
    }
    public Class1()
    {
        AppDomain adc2 = AppDomain.CreateDomain("adc2");
        class2 = (Class2)adc2.CreateInstanceAndUnwrap(typeof(Class2).Assembly.FullName, typeof(Class2).FullName);
    }
}
public class Class2 : MarshalByRefObject
{
    static List<int> myIntegers = new List<int>();

    public static List<int> MyIntegers
    {
        get { return Class2.myIntegers; }
        set { Class2.myIntegers = value; }
    }
    public void AddInteger(int integer)
    {
        myIntegers.Add(integer);
    }
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        foreach (int integer in myIntegers)
        {
            sb.AppendLine(integer.ToString());
        }
        return sb.ToString();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Type type1 = typeof(Class1);
        AppDomain ad1 = AppDomain.CreateDomain("ad1");
        Class1 ad1t1 = (Class1)ad1.CreateInstanceFromAndUnwrap(type1.Assembly.Location, type1.FullName);

        AppDomain ad2 = AppDomain.CreateDomain("ad2");
        Class1 ad2t1 = (Class1)ad2.CreateInstanceFromAndUnwrap(type1.Assembly.Location, type1.FullName);

        ad1t1.Class2.AddInteger(0);
        ad2t1.Class2.AddInteger(1);
        Console.WriteLine(ad1t1.Class2.ToString()); //Output: 0
        Console.WriteLine(ad2t1.Class2.ToString()); //Output: 1
        //
        Console.ReadKey();
    }
}

Edit

Ok I found out that my plugin loader is to blame. Can someone say why you cant work across appdomains if you will use different Pluginloaders (or at least one loader)?

If all files are in one Assembly it will work (increment is 3). In my scenario (many different assemblies) I only get 1,1,1

If someone need more information to help me, feel free to ask for it.

Example 1 (Each Instance count for itself):

Assembly: Main

PluginLoader.PluginLoader<IPlugin> pluginLoader1 = new PluginLoader.PluginLoader<IPlugin>(new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)));
IPlugin cl1 = pluginLoader1.Activate("MyLibrary.dll", "MyLibrary.Class1");
PluginLoader.PluginLoader<IPlugin> pluginLoader2 = new PluginLoader.PluginLoader<IPlugin>(new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)));
IPlugin cl3 = pluginLoader2.Activate("MyLibrary2.dll", "MyLibrary2.Class3");
//Increment() increases a static variable starting by 0
cl1.Increment();
Console.WriteLine(cl1.ToString()); //Output: 1
cl3.Increment();
Console.WriteLine(cl3.ToString()); //Output: 1

ClassLibrary2.Class2 class2 = new ClassLibrary2.Class2();
class2.Increment();
Console.WriteLine(class2.ToString()); //Output: 1

Assembly: ClassLibrary2

[Serializable]
public class Class2 : IPlugin
{
    public Class2() { }

    public override string ToString()
    {
        return incrementer.ToString();
    }
    static int incrementer = 0;
    public void Increment()
    {
        incrementer++;
    }
}

Assembly: MyLibrary

public class Class1 : MarshalByRefObject, IPlugin
{
    Class2 class2;

    public Class2 Class2
    {
        get { return class2; }
        set { class2 = value; }
    }
    public Class1()
    {
        PluginLoader.PluginLoader<Class2> pluginLoader = new PluginLoader.PluginLoader<Class2>(new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)));
        class2 = pluginLoader.Activate("ClassLibrary2.dll", "ClassLibrary2.Class2");
        //AppDomain adc2 = AppDomain.CreateDomain("adc2");
        //class2 = (Class2)adc2.CreateInstanceAndUnwrap(typeof(Class2).Assembly.FullName, typeof(Class2).FullName);
    }
    public void Increment()
    {
        this.class2.Increment();
    }
    public override string ToString()
    {
        return AppDomain.CurrentDomain.FriendlyName+": "+ this.class2.ToString();
    }
}

Assembly: MyLibrary2

public class Class3 : MarshalByRefObject, IPlugin
{
    Class2 class2;

    public Class2 Class2
    {
        get { return class2; }
        set { class2 = value; }
    }
    public Class3()
    {
        PluginLoader.PluginLoader<Class2> pluginLoader = new PluginLoader.PluginLoader<Class2>(new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)));
        class2 = pluginLoader.Activate("ClassLibrary2.dll", "ClassLibrary2.Class2");
        //AppDomain adc2 = AppDomain.CreateDomain("adc2");
        //class2 = (Class2)adc2.CreateInstanceAndUnwrap(typeof(Class2).Assembly.FullName, typeof(Class2).FullName);
    }
    public void Increment()
    {
        this.class2.Increment();
    }
    public override string ToString()
    {
        return AppDomain.CurrentDomain.FriendlyName + ": " + this.class2.ToString();
    }
}

Assembly: PluginInterface

public interface IPlugin
{
    void Increment();
}

Assembly: PluginLoader

public class PluginLoader<T> where T : IPlugin
{
    //Here are placed all Fields
    #region Fields
    string path;
    System.AppDomain appDomain;
    #endregion

    //Here are placed all Properties
    #region Properties
    public List<KeyValuePair<String, String>> Plugins
    {
        get { return (List<KeyValuePair<String, String>>)appDomain.GetData("Plugins"); }
    }
    #endregion

    //Here are placed all Constructors
    #region Constructors
    public PluginLoader(DirectoryInfo path)
    {
        this.path = path.FullName;
        if (!path.Exists)
            path.Create();
        AppDomainSetup appDomainSetup = new AppDomainSetup();
        appDomainSetup.AppDomainInitializer = new AppDomainInitializer(GetInterfaceTypes);
        appDomainSetup.AppDomainInitializerArguments = new string[] { this.path };
        appDomain = AppDomain.CreateDomain(typeof(T).Name, null, appDomainSetup);
    }
    #endregion

    #region Methods
    private static void GetInterfaceTypes(string[] args)
    {
        AppDomain appDomain = System.AppDomain.CurrentDomain;
        string[] files = Directory.GetFiles(args[0], "*.dll", SearchOption.AllDirectories);
        List<KeyValuePair<String, String>> infos = new List<KeyValuePair<String, String>>();
        foreach (string file in files)
        {
            try
            {
                Assembly asm = Assembly.LoadFrom(file);
                foreach (Type type in asm.GetTypes())
                {
                    if (typeof(T).IsAssignableFrom(type))
                        infos.Add(new KeyValuePair<string, string>(file, type.FullName));
                }
            }
            catch (Exception ex)
            {
            }
        }
        appDomain.SetData("Plugins", infos);
    }
    public virtual T Activate(String assemblyFile, String type, params object[] args)
    {
        try
        {
            T instance = (T)this.appDomain.CreateInstanceFromAndUnwrap(Path.Combine(this.path, Path.GetFileName(assemblyFile)), type, args);
            return instance;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    #endregion
}

Example 2 (Each instance count the same increment variable):

Put all classes in one assembly.

public interface IPlugin
{
    void Increment();
}
[Serializable]
public class Class1 : IPlugin
{
    public Class1() { }
    static int incrementer = 0;
    public void Increment()
    {
        incrementer++;
    }
    public override string ToString()
    {
        return incrementer.ToString();
    }
}
class PluginLoader<T> where T : IPlugin
{
    //Here are placed all Fields
    #region Fields
    string path;
    System.AppDomain appDomain;
    #endregion

    //Here are placed all Properties
    #region Properties
    public List<KeyValuePair<String, String>> Plugins
    {
        get { return (List<KeyValuePair<String, String>>)appDomain.GetData("Plugins"); }
    }
    #endregion

    //Here are placed all Constructors
    #region Constructors
    public PluginLoader(DirectoryInfo path)
    {
        this.path = path.FullName;
        if (!path.Exists)
            path.Create();
        AppDomainSetup appDomainSetup = new AppDomainSetup();
        appDomainSetup.AppDomainInitializer = new AppDomainInitializer(GetInterfaceTypes);
        appDomainSetup.AppDomainInitializerArguments = new string[] { this.path };
        appDomain = AppDomain.CreateDomain(typeof(T).Name, null, appDomainSetup);
    }
    #endregion

    #region Methods
    private static void GetInterfaceTypes(string[] args)
    {
        AppDomain appDomain = System.AppDomain.CurrentDomain;
        string[] files = Directory.GetFiles(args[0], "*.dll", SearchOption.AllDirectories);
        List<KeyValuePair<String, String>> infos = new List<KeyValuePair<String, String>>();
        foreach (string file in files)
        {
            try
            {
                Assembly asm = Assembly.LoadFrom(file);
                foreach (Type type in asm.GetTypes())
                {
                    if (typeof(T).IsAssignableFrom(type))
                        infos.Add(new KeyValuePair<string, string>(file, type.FullName));
                }
            }
            catch (Exception ex)
            {
            }
        }
        appDomain.SetData("Plugins", infos);
    }
    public virtual T Activate(String assemblyFile, String type, params object[] args)
    {
        try
        {
            T instance = (T)this.appDomain.CreateInstanceFromAndUnwrap(Path.Combine(this.path, Path.GetFileName(assemblyFile)), type, args);
            return instance;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    #endregion
}
class Program
{
    static void Main(string[] args)
    {
        string file = System.Reflection.Assembly.GetExecutingAssembly().Location;
        string path = Path.GetDirectoryName(file);

        PluginLoader<IPlugin> pluginLoader1 = new PluginLoader<IPlugin>(new DirectoryInfo(path));
        IPlugin cl1 = pluginLoader1.Activate(file, "AppDomainCheck.Class1");
        PluginLoader<IPlugin> pluginLoader2 = new PluginLoader<IPlugin>(new DirectoryInfo(path));
        IPlugin cl3 = pluginLoader1.Activate(file, "AppDomainCheck.Class1");

        cl1.Increment();
        Console.WriteLine(cl1.ToString()); //Output: 1
        cl3.Increment();
        Console.WriteLine(cl3.ToString()); //Output: 2

        Console.ReadKey();
    }
}
Mitja
  • 863
  • 5
  • 22
  • This might be helpful to you http://stackoverflow.com/a/9807826/1505865 – Jenish Rabadiya Feb 27 '15 at 09:14
  • I read this comment too, but when I tried it in my application, it wont work. So something else is not equal with my code example (In the example above it works). – Mitja Feb 27 '15 at 09:22
  • sorry pasted wrong link here your answer seems to be happening in his question :) http://stackoverflow.com/q/4298913/1505865 – Jenish Rabadiya Feb 27 '15 at 09:29
  • Yes thats the trick of using Serializable instead of MarshalByRefObject that will work in my example above too. But in my main application it is not working. So at the moment I try to find the differences. – Mitja Feb 27 '15 at 09:33

1 Answers1

3

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.

rducom
  • 7,072
  • 1
  • 25
  • 39
  • If I use a serializable class instead of MarshalByRefObject I will get a copy of my static variable in every appdomain. But in my scenario it is not working. – Mitja Feb 27 '15 at 11:32
  • In the code you posted, each time you create a new appdomain, so each time you increment a value one time on the newly created appdomain, the result is 1. IMO, this behavior is normal. – rducom Feb 27 '15 at 11:49
  • If I put all files in one Assembly, the int will increment up to 3. – Mitja Feb 27 '15 at 12:07
  • If I put all classes in different assemblies, the integer will count for each appDomain. Why? – Mitja Feb 27 '15 at 12:16