C#,获取Activator.CreateInstance(assemblyType)对象

时间:2016-11-29 00:36:27

标签: c# late-binding

我的控制台应用程序位于 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日)

服务器应用参考:

  • Library.Infrastructure
  • QueueApp.Core
  • 迟发型
  • OWIN

职位库参考:

  • Library.Infrastructure
  • Library.Persistence
  • Library.SQL.Database01
  • Library.SQL.Database02
  • QueueApp.Job.Core
  • 的EntityFramework

我们有几个遵循相同模式的 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

3 个答案:

答案 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中加载Job001Job002

请注意,动态加载程序集的顺序是非常重要:

  • 加载Job001时,如果找到它会自动加载LibraryA, 1.0.0.0,如果在此之后加载Job002,它将无法加载LibraryA, 2.0.0.0LibraryA, 1.0.0.0将保留在域中。

  • 同样,当您加载Job002时,如果找到它会自动加载LibraryA, 2.0.0.0,如果您在此之后加载Job001,它将无法加载加载LibraryA, 1.0.0.0LibraryA, 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;
    }
}

到目前为止似乎运作良好。