Доброе время суток Коллеги!
возникла некоторирая сложность при работе и не приходит в голову как разрезолвить данную проблему
работа с плагинами
есть интерфейс :
1 сборка
public interface IPlugin
{
bool Start();
event EventHandler<LogEventArgs> OnLog;
}
public class LogEventArgs : EventArgs
{
public string Message { get; set; }
public Exception Error { get; set; }
}
2 сборка сам плагин
public class Test: MarshalByRefObject,IPlugin
{
public bool Start()
{
if(OnLog!=null)
OnLog(this,new LogEventArgs(){Message = "Started"});
return true;
}
public event EventHandler<LogEventArgs> OnLog;
}
3 сборка
использование плагина
class Program
{
static void Main(string[] args)
{
var pl = new PluginController();
Console.WriteLine("before load...");
foreach (var each in AppDomain.CurrentDomain.GetAssemblies().Where(x => typeof(IPlugin).IsAssignableFrom(x.GetTypes().FirstOrDefault())))
{
Console.WriteLine(each.FullName);
}
pl.LoadAllPlugin();
Console.WriteLine("after load...");
foreach (var each in AppDomain.CurrentDomain.GetAssemblies().Where(x => typeof(IPlugin).IsAssignableFrom(x.GetTypes().FirstOrDefault())))
{
Console.WriteLine(each.FullName);
}
pl.UnloadAllPlugin();
Console.WriteLine("after unload...");
foreach (var each in AppDomain.CurrentDomain.GetAssemblies().Where(x => typeof(IPlugin).IsAssignableFrom(x.GetTypes().FirstOrDefault()) ))
{
Console.WriteLine(each.FullName);
}
Console.ReadKey();
}
}
public class PluginController
{
private List<PluginObject> plList;
public PluginController()
{
plList = new List<PluginObject>();
}
public void LoadAllPlugin()
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
var pluginsFolder = WebConfigurationManager.AppSettings["PluginDirectory"];
var domainInfo = new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
PrivateBinPath = pluginsFolder
};
var domain = AppDomain.CreateDomain("PluginLoader", null, domainInfo);
foreach (var plugin in Directory.GetFiles(pluginsFolder, "B*.dll", SearchOption.TopDirectoryOnly))
{
var newAssembly = Assembly.ReflectionOnlyLoadFrom(plugin);
var types = newAssembly.GetTypes();
var tx = types.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
if (tx == null) return;
var exe = domain.CreateInstanceFromAndUnwrap(tx.Assembly.Location,tx.FullName) as IPlugin;
if (exe != null)
{
exe.OnLog += exe_OnLog;
exe.Start();
plList.Add(new PluginObject(){Domain = domain,plugin = exe});
}
}
}
Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
return Assembly.ReflectionOnlyLoad(args.Name);
}
public void UnloadAllPlugin()
{
plList.ForEach(x=>AppDomain.Unload(x.Domain));
}
static void exe_OnLog(object sender, LogEventArgs e)
{
Console.WriteLine(e.Message);
}
}
В итоге вижу :
before load...
Started
after load...
IPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
after unload...
IPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
проблема в том что плагин как мы видим не выгрузился.
в чем и заключается вопрос,как правильнее быть?
p\s но если попытаюсь после анлоада обратиться к нему будет исключение что он выгружен,но тогда почему мы видим что это не так на консоли
Здравствуйте, codenet, Вы писали:
C>В итоге вижу :
C>C>before load...
C>Started
C>after load...
C>IPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
C>after unload...
C>IPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
C>
C>проблема в том что плагин как мы видим не выгрузился.
C>в чем и заключается вопрос,как правильнее быть?
Плагин это у вас Test. Где он здесь?
C>p\s но если попытаюсь после анлоада обратиться к нему будет исключение что он выгружен,но тогда почему мы видим что это не так на консоли
Почему нет? Выгрузили один домен, а список сборок берется из другого.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>Здравствуйте, codenet, Вы писали:
C>>В итоге вижу :
C>>C>>before load...
C>>Started
C>>after load...
C>>IPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
C>>after unload...
C>>IPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
C>>
C>>проблема в том что плагин как мы видим не выгрузился.
C>>в чем и заключается вопрос,как правильнее быть?
TK>Плагин это у вас Test. Где он здесь?
C>>p\s но если попытаюсь после анлоада обратиться к нему будет исключение что он выгружен,но тогда почему мы видим что это не так на консоли
TK>Почему нет? Выгрузили один домен, а список сборок берется из другого.
Да,TK, Вы правы,спасибо за ответ!, запутался малость
,действительно сборки мы загружаем в созданные домены и искать их в CurrentDomain было глупо.
Здравствуйте, codenet, Вы писали:
C>проблема в том что плагин как мы видим не выгрузился.
Уже ответили. В коде другая проблема: exe_OnLog будет вызываться из домена плагина.
Если есть желание вынести плагины в отдельный домен, то лучше не вызывать методы плагина из хост-домена напрямую. Используйте обёртку, на время отладки задавайте friendly name для аппдомена и пишите вызовы в лог.
Не будет косяков из-за неправильной реализации плагина типа таких:
https://nbevans.wordpress.com/2011/04/17/memory-leaks-with-an-infinite-lifetime-instance-of-marshalbyrefobject/
http://stackoverflow.com/q/2410221/318263
Ну и отлаживать проще будет.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, codenet, Вы писали:
C>>проблема в том что плагин как мы видим не выгрузился.
S>Уже ответили. В коде другая проблема: exe_OnLog будет вызываться из домена плагина.
S>Если есть желание вынести плагины в отдельный домен, то лучше не вызывать методы плагина из хост-домена напрямую. Используйте обёртку, на время отладки задавайте friendly name для аппдомена и пишите вызовы в лог.
S>Не будет косяков из-за неправильной реализации плагина типа таких:
S>https://nbevans.wordpress.com/2011/04/17/memory-leaks-with-an-infinite-lifetime-instance-of-marshalbyrefobject/
S>http://stackoverflow.com/q/2410221/318263
S>Ну и отлаживать проще будет.
Спасибо за советы!
а не подскажите еще один нюанс, могу конечно и без него обойтись,но все же хотелось бы знать как поступить правильнее.
в случае когда я загружаю сборку в домен таким образом :
var domain = AppDomain.CreateDomain(Path.GetFileName(plugin), null, domainInfo);
var newAssembly = Assembly.LoadFrom(plugin);
var types = newAssembly.GetTypes();
var tx = types.FirstOrDefault(x => typeof(IPlugin).IsAssignableFrom(x));
var exe = domain.CreateInstanceFromAndUnwrap(tx.Assembly.Location,tx.FullName) as IPlugin;
все как бы Ок,но сборки загружаются также и в текущий домен о чем говорит AppDomain.CurrentDomain.GetAssemblies()
Но тут я могу проверить —
types.FirstOrDefault(x => typeof(IPlugin).IsAssignableFrom(x));
является ли сборка моим плагином т.е реализует ли интерфейс
,а в случае если я использую такой подход :
var domain = AppDomain.CreateDomain(Path.GetFileName(plugin), null, domainInfo);
var newAssembly = Assembly.ReflectionOnlyLoadFrom(plugin);
var types = newAssembly.GetTypes();
var tx = types.FirstOrDefault();
var exe = domain.CreateInstanceFromAndUnwrap(tx.Assembly.Location,tx.FullName) as IPlugin;
я получаю все тоже самое, Но сборка плагина не грузится в текущий домен,а только в созданный мною,что логично, но у меня пропадает возможность проверки
как была в первом случае —
types.FirstOrDefault(x => typeof(IPlugin).IsAssignableFrom(x));
как в подходе с ReflectionOnlyLoadFrom грамотнее проверить что плагин валидный ,а не сторонняя длл