gpt4 book ai didi

c# - 如何监视应用程序域中影子复制程序集的原始版本何时更改

转载 作者:行者123 更新时间:2023-11-30 18:27:52 25 4
gpt4 key购买 nike

我正在创建一个服务,在单独的应用程序域中托管和运行其他较小的服务(有点像迷你 IIS)。当每个服务在启动时注册时,我运行以下代码:

AppDomainSetup setup = new AppDomainSetup
{
LoaderOptimization = LoaderOptimization.MultiDomain,
ShadowCopyDirectories = service.FullPath, // Directory service binary lives in
ShadowCopyFiles = Convert.ToString(true)
};

AppDomain domain = AppDomain.CreateDomain(service.Name, null, setup);
ServiceDomain s = domain.CreateInstanceAndUnwrap<ServiceDomain>();
s.RegisterService(service, DefaultPort);

这基本上设置了一个应用程序域,启用卷影复制,并在新的应用程序域中调用 RegisterServiceRegisterService 方法将创建一个对象的实例,它将影子复制程序集加载到内存中。

例如,服务可能在程序集 Foo.dll 中包含类型 ServiceFooFoo.dll 位于 service.FullPath 中,因此在加载应用程序域时会复制影子。从这一点开始,我可以删除或修改 service.FullPath 目录中的原始Foo.dll,这太棒了。

但是,当有人修改原始Foo.dll(例如,他们通过网络复制了一个新版本)时,我希望得到通知,以便我可以卸载旧的应用程序域并使用新版本的程序集重新创建它。

基本上,我在这里试图做的是让管理员能够部署新版本的服务,而不会中断在同一进程中运行的其他服务。

我的问题:Foo.dll 被修改时,或者 ShadowCopyDirectories 目录中的任何文件发生变化时,我如何得到通知?我确信我可以每隔几秒检查一次这些目录中的时间戳,但似乎应该有更好的方法。这种情况的最佳方法是什么?

更新:

我目前的想法都是围绕 FileWatcher 解决的。然而,弄清楚要观看的文件对我来说很困难。

想法 1: 监控 service.FullPath,这是包含可能更改的文件的目录。但是,多个服务可能位于此目录中。如果我监控该目录,对一个文件的更改可能会导致实际上不使用该文件的服务错误地重新启动。

想法 2: 解析 service.TypeName,这是一个包含完全限定类型名称的字符串。 .NET 提供了解析这些字符串的方法,例如 Type.GetType(string),这样我就可以访问代码库。但是,第二次我将程序集加载到父应用程序域中时,它就被锁定了,所以我不能再更改它了。我可以尝试手动解析 TypeName,但这种语法相当复杂。有很多库试图解析这种语法。

想法 3: 监控 AssemblyLoad 事件:

domain.AssemblyLoad += (sender, args) =>
{
string test = AppDomain.CurrentDomain.FriendlyName;
Uri fileUri = new Uri(args.LoadedAssembly.CodeBase);
FileInfo fileInfo = new FileInfo(fileUri.LocalPath);
};

当域加载程序集时会被触发。我可以检测 fileInfo.Directory 是否与 service.FullPath 相同,如果是,则在该文件上设置监视。一个问题。此委托(delegate)在 domain 的上下文中运行。我无权访问 ServiceResolver 或根域中的任何内容。

想法 4: 在我调用 s.RegisterService 之后,检查 domain.GetAssemblies() 并尝试找到来自 >服务.FullPath。但是,当我运行时:

var assemblies = domain.GetAssemblies();

它立即抛出异常:

Type 'System.Reflection.Emit.InternalAssemblyBuilder' in assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.

我不太清楚这是为什么。我猜 GetAssemblies() 只能在当前 AppDomain 上调用。

最佳答案

这就是我所做的。首先,我创建了一个名为 ServiceDomainProxy 的新类,如下所示:

internal class ServiceDomainProxy : MarshalByRefObject
{
private ServiceController controller;
private List<FileSystemWatcher> watchers;

public ServiceDomainProxy(ServiceController controller)
{
this.controller = controller;
}

public void CreateMonitor(Uri fileUri)
{
if(watchers == null)
watchers = new List<FileSystemWatcher>();

// Setup a FileWatcher on type, notify controller whenever it reloads
FileInfo file = new FileInfo(fileUri.LocalPath);
FileSystemWatcher watcher = new FileSystemWatcher
{
Path = file.DirectoryName,
Filter = file.Name
};

watcher.Changed += fileChanged;
watcher.EnableRaisingEvents = true;
watchers.Add(watcher);
}

void fileChanged(object sender, FileSystemEventArgs e)
{
Controller.Log.Info("Reloading Service Assembly: {0}", e.Name);
// TODO: Call the Reload method on controller
}
}

这个类基本上充当域之间的代理。接下来,我修改了我的 ServiceDomain 类,使其具有一个采用 ServiceDomainProxy 实例的构造函数:

public ServiceDomain(ServiceDomainProxy proxy)
{
this.proxy = proxy;
}

我必须更新我的 CreateInstanceAndUnwrap 调用以传入 ServiceDomainProxy 实例:

Type t = typeof(ServiceDomain);
ServiceDomain s = domain.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, false, BindingFlags.Default, null, new object[] { proxy }, null, null);

然后,在 ServiceDomain.RegisterService 中,我可以使用 ServiceDomainProxy注册解析的类型:

proxy.CreateMonitor(new Uri(service.ResolveType().Assembly.CodeBase));

这会跨应用程序域边界调用 ServiceDomainProxy 的原始实例,然后它可以设置文件系统监视器并提醒 Controller 需要重新加载服务。

希望这对某人有帮助!

关于c# - 如何监视应用程序域中影子复制程序集的原始版本何时更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26025046/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com