我在停止一个Windows服务后用新版本替换了该服务的EXE。EXE绝对是新版本,Windows资源管理器属性显示新版本。
然而,程序集(当加载System.Reflection.Assembly.LoadFile
时)报告一个.FullName
,这似乎表明它不是最新版本。
这在我部署它的第一个站点上没有发生,所以现在我的版本报告系统似乎表明这个站点没有升级。(版本报告由一个单独的ASP管理。. NET web服务,它在某些目录下发布所有版本的程序集,正是这个web服务调用System.Reflection.Assembly.LoadFile
)。
这是什么?是否存在某种缓存(web服务将在昨天升级之前最后一次检查该程序集)?
代码非常基本:
System.IO.DirectoryInfo assemblies = new System.IO.DirectoryInfo(DirectoryName);
foreach (var f in assemblies.GetFiles(Pattern))
{
var a = System.Reflection.Assembly.LoadFile(f.FullName);
l.Add(new VersionManager.Assembly(f.Name, f.LastWriteTime, a.FullName));
}
return l; // l is List<VersionManager.Assembly>
删除EXE并替换它(而不是覆盖它)没有效果。
我写了一个小的控制台应用程序,并在最后一次查询web服务大约3.5小时后直接在系统上运行它,非常惊讶地发现它返回了正确的版本。然后我查询了web服务,它返回了正确的版本。
二进制文件或web服务没有任何变化(尽管它所属的web应用程序可能已被回收,但web服务本身并不存储它检索到的关于程序集的信息,它只是在响应中发送它们)
UPDATE - 2011-12-20:
这个问题再次发生。我部署到一个站点,然后在所有站点上运行我的库存程序,站点报告了正确的版本。我部署到另外两个站点,DLL上的属性给出了正确的版本,但是通过系统加载它报告的版本信息。对另外两个站点的反思已经过时了。它似乎在第一次调用后被缓存到某个地方,并在第二次调用时被重用。
有一个IIS ASP。净(。. NET 2.0) web服务,它报告系统目录(不属于web服务的目录)中组件的DLL版本。其中一个目录包含Windows服务的EXE和配置。当Windows服务被停止,EXE被替换,并询问web服务时,它有时报告旧版本。如果web服务在一段时间内没有被调用,那么在下一次查询中将正确报告替换EXE。如果web服务在替换之前被调用,它在替换之后不会报告任何变化,尽管EXE已经被替换,并且通过Windows资源管理器等报告正确的版本。
我注意到LoadFile没有伴随着任何类型的卸载。即使对程序集的引用超出了作用域,我想知道它是否保持加载状态(因此在随后的调用中不会刷新,从而仍然报告过期信息),直到IIS或其他使其失效。是否有卸载程序集的方法?是否有一种方法可以在不加载程序集文件的情况下获得程序集文件的版本信息?
使用System.Reflection.Assembly.LoadFile(FileName)加载程序集,如果不完全卸载AppDomain,则无法卸载程序集。
为了在不加载程序集的情况下检查程序集,另一种选择是:
var a = System.Reflection.Assembly.LoadFile(f.FullName);
:
var a = System.Reflection.AssemblyName.GetAssemblyName(f.FullName);
难道不应该使用Inspect Appdomain来解决这个问题吗?或者安全性不允许创建新域?
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create a new domain for loading extra Assemblies
AppDomain inspectDomain = AppDomain.CreateDomain("Inspect");
// attach handler to show that no assemblies are loaded in the current domain
AppDomain.CurrentDomain.AssemblyLoad +=new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
// another prove
int startcount = AppDomain.CurrentDomain.GetAssemblies().Length;
// now call the inspection method in the inspectionDomain
inspectDomain.DoCallBack(new CrossAppDomainDelegate(Inspect));
// what is the count on assemblies in the curent domain
int endcount = AppDomain.CurrentDomain.GetAssemblies().Length;
// show it
Console.WriteLine("Appdomain {2} # start: {0} and end {1}",
startcount,
endcount,
AppDomain.CurrentDomain.FriendlyName);
// unload our inspectdomain and therefor the loaded assemblies
AppDomain.Unload(inspectDomain);
// pause so we can see
Console.ReadLine();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
// write a line for a loaded assembly
Console.WriteLine("program domain: {0}", args.LoadedAssembly.FullName);
}
public static void Inspect()
{
Console.WriteLine("Inspecting...");
int startcount = AppDomain.CurrentDomain.GetAssemblies().Length;
var assemblies = new DirectoryInfo(
@"C:WindowsMicrosoft.NETFramework64v2.0.50727");
foreach (var f in assemblies.GetFiles("System.Data.*.dll"))
{
var a = System.Reflection.Assembly.LoadFile(f.FullName);
Console.WriteLine("name:{0}, rt:{1}", a.FullName, a.ImageRuntimeVersion);
}
int endcount = AppDomain.CurrentDomain.GetAssemblies().Length;
Console.WriteLine("Appdomain {2} start: {0} and end {1}",
startcount,
endcount,
AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("Done Inspecting...");
}
}
}