Выгрузить домен приложения при использовании IDisposable - PullRequest
0 голосов
/ 17 октября 2011

У меня есть маршализируемый класс, который содержит метод фабрики. Фабричный метод может использоваться для создания экземпляра класса в тестовом домене приложений. Я пытаюсь понять, могу ли я использовать класс с помощью шаблона (...) dispose.

Принципиальная проблема для меня заключается в том, загружен ли тестовый домен AppDomain и когда. Я добавил статический флаг в класс, который устанавливается при вызове метода экземпляра. Если домен приложения не выгружен, то я считаю, что этот флаг должен сохранить его настройку при последующих вызовах.

Пример класса и консольного приложения для тестирования будет выглядеть следующим образом:

using System;
using System.Reflection;
using System.Threading;

namespace AppDomainInDispose
{
    public class TestClass : MarshalByRefObject, IDisposable
    {
        public void Run()
        {
            Console.WriteLine("Hello from {0}", Thread.GetDomain().FriendlyName);
            if (_flag)
                Console.WriteLine("Flagged!");
            else
                _flag = true;
        }

        private static bool _flag = false;

        public static TestClass InstantiateInTestDomain()
        {
            var callingDomain = Thread.GetDomain();
            var setup = new AppDomainSetup() { ApplicationBase = callingDomain.SetupInformation.ApplicationBase };
            _domain = AppDomain.CreateDomain("test-domain", null, setup);
            _domain.DomainUnload += _domain_DomainUnload;

            var assembly = Assembly.GetAssembly(typeof(TestClass)).CodeBase;
            var proxy = _domain.CreateInstanceFromAndUnwrap(assembly, "AppDomainInDispose.TestClass") as TestClass;

            return proxy;
        }

        static void _domain_DomainUnload(object sender, EventArgs e)
        {
            Console.WriteLine("Unloading");
        }

        public static AppDomain _domain = null;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~TestClass()
        {
            Dispose(false);
        }

        private void Dispose(bool disposing)
        {
            if(disposing)
            {
                //AppDomain.Unload(_domain);    // can't, as I'm in the AppDomain
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using(var testClass = TestClass.InstantiateInTestDomain())
            {
                testClass.Run();
            }
            using (var testClass = TestClass.InstantiateInTestDomain())
            {
                testClass.Run();    // if the appdomain hasn't been unloaded then the static flag will still be set
            }

            Console.ReadKey();
        }
    }
}

кажется , что в этом ограниченном тесте AppDomain выгружается - но я не уверен, где. Может кто-нибудь объяснить, что происходит с AppDomain? Этот подход очень плохая идея?

РЕДАКТИРОВАТЬ: похоже, вы можете иметь несколько доменов приложений с одним и тем же именем, что я не понял. Кроме того, событие DomainUnload не запускается, поэтому я могу предположить, что домен не выгружается. Это или событие не запускается при некоторых обстоятельствах (возможно, когда процесс хостинга завершается).

1 Ответ

2 голосов
/ 17 октября 2011

Хммм ... это становится немного сложнее. Если вы хотите быть уверены, что домен приложения выгружается после окончания использования блока, ниже приведена одна из таких реализаций. Здесь я разделил две проблемы: «создание домена приложения и обработка домена приложения» и «сам тип, который загружается в домен приложения». (Все это в основной проге класса)

public class GenericDisposable<T> : IDisposable
{
    public Action Dispose { get; set; }
    public T Object { get; set; }
    void IDisposable.Dispose()
    {
        Dispose();
    }

}
public static GenericDisposable<T> CreateDomainWithType<T>()
{
    var appDomain = AppDomain.CreateDomain("test-domain");
    var inst = appDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
    appDomain.DomainUnload += (a, b) => Console.WriteLine("Unloaded");
    return new GenericDisposable<T>() { Dispose = () => AppDomain.Unload(appDomain), Object = (T)inst };
}
public class User : MarshalByRefObject
{
    public void Sayhello()
    {
        Console.WriteLine("Hello from User");
    }
}

//Usage              
static void Main()
{
    using (var wrap = CreateDomainWithType<User>())
    {
        wrap.Object.Sayhello();
    }
    Console.Read();
}
...