Делаем отгружаемые сборки: взаимодействуем между доменами без маршаллинга
сегодня в 16:47
Для начала, полный список выложенных на Хабре статей данного цикла
Ссылка на проект в GitHub: DotNetEx
На множественных ресурсах время от времени задается вопрос. Можно ли сделать отгружаемые сборки с текущего домена? Так, чтобы попользовался и «давай, до свидания!»? Везде и всегда ответ, который давался — это «нет». Ведь единственное, что можно выгрузить — это домен. Соответственно, если хочется наладить отгрузку, сборку надо помещать в домен, и налаживать между доменами взаимодействие через сериализуемые типы. А это — очень медленное взаимодействие. А мы скажем так. Можно. С ньюансами. Загружать мы будем также в отдельный домен. Но отменим сериализацию при вызове методов между доменами.
Вопросы, которые мы будем решать:
Создание домена с возможностью отдачи объекта из домена в родительский Выгрузка сборки Решение проблемы Итак, как всегда, будем решать проблемы по мере появления: Как мы уже выяснили в прошлых статьях, память общая и не зависит от доменов. А это значит, что если найти способ передачи указателя на объект, то можно научиться без сериализации передавать объекты между доменами.Возьмем некий общий тип. Для упрощения, возьмем тип из mscorlib: IServiceProvider.Создадим сборку, которую мы собираемся подружить с возможностью отгрузки:
public class Implementation: IServiceProvider { public override string ToString () { return «Awesome»; }
public object GetService (Type serviceType) { return new object (); } } Теперь напишем класс, который будет создавать домен и уметь создавать в этом домене экземпляры классов: public class AppDomainRunner: MarshalByRefObject, IDisposable { private AppDomain appDomain; private Assembly assembly; private AppDomainRunner remoteRunner;
private void LoadAssembly (string assemblyPath) { assembly = Assembly.LoadFile (assemblyPath); }
public AppDomainRunner (string assemblyPath) { // make appdomain appDomain = AppDomain.CreateDomain («PseudoIsolated», null, new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory });
// create object instance remoteRunner = (AppDomainRunner)appDomain.CreateInstanceAndUnwrap (typeof (AppDomainRunner).Assembly.FullName, typeof (AppDomainRunner).FullName); remoteRunner.LoadAssembly (assemblyPath); }
public IntPtr CreateInstance (string typename) { return remoteRunner.CreateInstanceImpl (typename); }
private IntPtr CreateInstanceImpl (string typename) { return EntityPtr.ToPointer (assembly.CreateInstance (typename)); }
public void Dispose () { assembly = null; remoteRunner = null; AppDomain.Unload (appDomain); }
Теперь напишем класс для IoC rконтейнера: public class Container: IDisposable { private AppDomainRunner appdomain;
private Dictionary
public Container (string assemblyName) { appdomain = new AppDomainRunner (Path.Combine (System.Environment.CurrentDirectory, assemblyName)); }
public void Register
public TInterface Resolve
public void Dispose ()
{
appdomain.Dispose ();
}
}
И последнее — использующий код:
static void Main (string[] args)
{
using (var container = new Container («library.dll»))
{
container.Register
Console.WriteLine («calling method without proxy: {0}», serviceProvider); Console.WriteLine («Current domain assemblies: {0}», string.Join (»,», AppDomain.CurrentDomain.GetAssemblies ().Select (asm => asm.GetName ().Name).ToArray ())); } } Выводы Как говорится, сделать отгружаемые типы нельзя, но если хочется, то можно. Необходимо просто как-то передать между доменами указатель на объект и им можно будет на равных правах пользоваться.Минус способа только один: мы не имеем права использовать объекты после выгрузки сборки. Это минус по одной простой причине: надо дополнительно контролировать порядок отгрузки и потери ссылки на объекты. Но, в общем случае это не проблема =)
—
1283
27
Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.