Я внедряю модульную систему для своего IRC-бота С#. Модули представляют собой сборки .dll, которые хранятся в подкаталоге «modules» и используются для добавления функциональности боту, например добавления дополнительных команд в IRC. Эти модули предназначены для загрузки и выгрузки во время выполнения, поэтому я могу обновлять бота или исправлять ошибки без перезапуска всего приложения.
В настоящее время модульная система создает новый AppDomain
для каждого загружаемого модуля, а прокси создается с использованием CreateInstanceFromAndUnwrap
внутри класса с именем ModuleHelper
.
AppDomain domain = AppDomain.CreateDomain(name, null, new AppDomainSetup
{
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
DisallowApplicationBaseProbing = true,
PrivateBinPathProbe = ModuleDirectory,
PrivateBinPath = ModuleDirectory,
ShadowCopyDirectories = ModuleDirectory,
CachePath = Path.Combine(ModuleDirectory, "cache"),
ShadowCopyFiles = bool.TrueString
});
ModuleProxy proxy = null;
try
{
proxy = (ModuleProxy)domain.CreateInstanceFromAndUnwrap(location, AssemblyName.GetAssemblyName(location).Name + ".Module");
proxy.OnLoad();
}
catch
{
AppDomain.Unload(domain);
throw;
}
Этот прокси наследуется от MarshalByRefObject
.
public abstract class ModuleProxy : MarshalByRefObject
{
internal protected virtual void OnLoad()
{
}
internal protected virtual void OnUnload()
{
}
}
OnLoad
и OnUnload
вызываются при загрузке или выгрузке модуля. Модули также наследуют от MarshalByRefObject
во внешней сборке, например, этот класс в модуле ConfigurationReader.dll
.
public class Module : ModuleProxy
{
private Configuration _configuration = new Configuration();
protected override void OnLoad()
{
string fileName = Path.Combine(ModuleHelper.ModuleDirectory, AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Name + ".conf");
_configuration.ReadAndLoadConfiguration(fileName);
IrcBot bot = new IrcBot(_configuration);
if (_configuration.Perform != null)
{
bot.EventManager.OnRegister += PerformOnRegister;
}
if (!string.IsNullOrWhiteSpace(_configuration.IdentifyMatchPeer + _configuration.IdentifyMatchText + _configuration.IdentifyPassword))
{
bot.EventManager.OnNotice += IdentifyOnNotice;
}
IrcBot.Bots.Add(_configuration.Id, bot);
IrcBot.Bots[_configuration.Id].Start();
}
...
...
...
Проблема в том, что когда я изменяю что-то, принадлежащее основному домену приложения (в частности, добавляя нового бота в коллекцию IrcBot.Bots, IrcBot.Bots.Add(_configuration.Id, bot);
), количество IrcBot.Bots
увеличивается только внутри вторичного домена приложения, а не в основном домене приложения, как я этого хочу. быть.
Немного поработав с Console.WriteLining, я обнаружил, что вызов IrcBot.Bots.Count
после вызова Add
во вторичном домене приложения возвращает 1, а повторный вызов сразу после вызова OnLoad
в основном домене приложения возвращает 0. Это имеет катастрофические последствия и вызывает другие модули, которые загружаются впоследствии, выходят из строя. Как я могу обновить количество ботов (среди прочего) в основном AppDomain после его изменения в дополнительном AppDomain?