我的控制台应用程序位于 C:\ MyApp 。
我有几个未被应用引用的库。我使用Activator.CreateInstance()来使用它们。它们位于 C:\ MyLibrary \ Job001,C:\ MyLibrary \ Job002等。这些库中的每一个都有多个依赖项,并且可以是主应用程序中已经存在的不同版本的依赖项。
当我尝试运行它时,我看到了这个错误:Could not load file or assembly 'Persistence.Database, Version=1.7.2.67, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
这是大多数作业的常见依赖项之一。我检查了目录,它确实存在于库中。
如何激活库 AND 让它使用在其自己的目录中找到的引用?
我使用以下(扩展名)代码来激活库:
public static IJob ConcreteJob(this JobInfoPayload src)
{
if (src.AssemblyFile.IsNullOrEmpty())
throw new Exception("AssemblyFile cannot be empty or null!");
if (src.AssemblyName.IsNullOrEmpty())
throw new Exception("AssemblyName cannot be empty or null!");
try
{
var assembly = Assembly.LoadFile(src.AssemblyFile);
var assemblyType = assembly.GetType(src.AssemblyName);
var job = Activator.CreateInstance(assemblyType) as IJob;
return job;
}
catch (Exception ex)
{
Serilog.Log.Logger.Fatal(ex, "JOB was not able to be created!!");
throw; // bubble this up to the top...
}
}
我正在查看system.appdomain.assemblyresolve,但我没有理解如何在库项目中使用它。
思想?
附加信息(2016年11月29日)
服务器应用参考:
职位库参考:
我们有几个遵循相同模式的 Jobs BUT 可以使用不同版本的作业库参考构建。这是由于随着时间的推移缓慢蠕变。如果去年写的工作仍然有效,我们为什么要花时间打开这个解决方案,更新所有参考资料,重新编译,然后花一个月时间通过质量保证和接受,当我们可以不管它时?
我遇到的挑战是JOB无法找到引用的文件,期望它们位于Server App目录中。相反,它们位于Job的目录中。使用Fuslogvw.exe只是确认它没有查看DLL的目录,而是查看托管应用程序的目录。
**无论是使用Assembly.LoadFrom()还是Assembly.LoadFile(),我目前都会遇到相同的行为。
FUSLOGVW日志结果:
*** Assembly Binder Log Entry (11/29/2016 @ 10:20:21 AM) ***
The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable D:\Dev\QueueApp\Source\QueueApp\bin\Debug\QueueApp.exe
--- A detailed error log follows.
=== Pre-bind state information ===
LOG: DisplayName = Job.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null (Fully-specified)
LOG: Appbase = file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = QueueApp.exe
Calling assembly : Job.AgileExport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: D:\Dev\QueueApp\Source\QueueApp\bin\Debug\QueueApp.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core.DLL.
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core/Job.Core.DLL.
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core.EXE.
LOG: Attempting download of new URL file:///D:/Dev/QueueApp/Source/QueueApp/bin/Debug/Job.Core/Job.Core.EXE.
LOG: All probing URLs attempted and failed.
APP正在寻找所有文件:
d:\开发\ QueueApp \来源\ QueueApp \ BIN \调试
JOB存在于:
D:\ Dev \ QueueApp \ Source \ Job.AgileExport \ bin \ Debug
答案 0 :(得分:1)
我认为有两种解决方案可供选择。
一种解决方案是创建一个新的AppDomain来托管动态加载的程序集。创建新的AppDomain时,您可以选择为AppDomain提供设置对象,在该对象中,您可以提供AppDomain用于解析程序集的路径。您无法更改现有 AppDomain中的路径,因为它已存在。
另一种解决方案是处理当前AppDomain的AssemblyResolve事件,该事件将在正常程序集解析失败的情况下引发。然后,您可以采取自定义步骤来帮助解决装配。
.NET中存在一个功能/错误,当.NET托管在各种容器(例如IE,COM +等)中时,需要处理此事件,而BinaryFormatter用于反序列化应该>的类型em>可用,但实际上找不到。
我有一个在此处挂钩和解析AssemblyResolve事件的示例: https://github.com/MarimerLLC/csla/blob/V1-5-x/cslacs10/NetRun/Launcher.cs
在您的情况下,您可以更改我的ResolveEventHandler方法,以在最初加载动态程序集的文件夹中查找“缺少”程序集。
答案 1 :(得分:1)
使用Assembly.LoadFrom
,无法在同一个AppDomain中加载多个版本的相同的程序集。
因此,如果Job001
需要LibraryA, 1.0.0.0
(并且无法在运行时使用较新版本)且Job002
需要LibraryA, 2.0.0.0
,则您必须在其自己的AppDomain中加载Job001
和Job002
。
请注意,动态加载程序集的顺序是非常重要:
加载Job001
时,如果找到它会自动加载LibraryA, 1.0.0.0
,如果在此之后加载Job002
,它将无法加载LibraryA, 2.0.0.0
和LibraryA, 1.0.0.0
将保留在域中。
同样,当您加载Job002
时,如果找到它会自动加载LibraryA, 2.0.0.0
,如果您在此之后加载Job001
,它将无法加载加载LibraryA, 1.0.0.0
和LibraryA, 2.0.0.0
将保留在域中。
您最好的选择是使用Assembly.LoadFile
+ AppDomain.AssemblyResolve
自己加载依赖项(然后您可以在同一个AppDomain中拥有同一程序集的多个版本),或者您创建一个单独的AppDomain对于每个JobXXX
程序集,并自动加载依赖项。
答案 2 :(得分:0)
这是我到目前为止所提出的。这些类位于主服务器应用程序中,在任何JOB中都找不到。我们有几种不同类型的JOB,Ad Hoc是其中一种类型。通过将代码放在基类中,所有JOB处理程序现在都继承它。
public class JobAdHocHandler : BaseHandler, IJobHandler
{
public MinimumResultModel Handle(MinimumCommandModel message)
{
var result = new MinimumResultModel {Id = "-1", PayloadAsString = message.FullPayloadString};
try
{
var info = message.MinimumPayload.JobInfo;
SetupInstance(info); // <<-- SOLUTION (in BaseHandler)
var job = JobHandler.GetJob(info); // <<-- SOLUTION (in BaseHandler)
result.Id = BackgroundJob.Enqueue(() => job.Execute(null, message.FullPayloadString, JobCancellationToken.Null));
}
catch (Exception ex)
{
Log.Logger.Fatal(ex, ex.Message);
result.Exception = ex;
}
AppDomain.Unload(JobAppDomain);
return result;
}
public bool AppliesTo(JobType jobType) => jobType == JobType.AdHoc;
}
public class BaseHandler : MarshalByRefObject
{
protected internal AppDomain JobAppDomain;
protected internal BaseHandler JobHandler;
protected internal void SetupInstance(JobInfoPayload info)
{
var ads = new AppDomainSetup
{
ApplicationBase = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName,
DisallowBindingRedirects = false,
DisallowCodeDownload = true,
PrivateBinPath = info.JobClassName,
ApplicationName = info.JobName,
};
JobAppDomain = AppDomain.CreateDomain(info.JobName, null, ads);
JobHandler = (BaseHandler)JobAppDomain.CreateInstanceAndUnwrap(typeof(BaseHandler).Assembly.FullName, typeof(BaseHandler).FullName);
}
protected internal IJob GetJob(JobInfoPayload info)
{
var assembly = Assembly.LoadFrom(info.JobClassName + @"\" + info.JobClassName + ".dll");
var assemblyType = assembly.GetType(info.AssemblyName);
var job = Activator.CreateInstance(assemblyType) as IJob;
if (job == null)
throw new Exception("Unable to create job: " + info.JobClassName);
return job;
}
}
到目前为止似乎运作良好。