5

我在 C# .NET 4.0 Windows 服务应用程序中使用 ILMerge 和 Quartz.NET。该应用程序在不使用 ILMerge 的情况下运行良好,但现在我们即将发布,我想将所有 DLL 合并到一个可执行文件中。

问题是,ILMerge 似乎工作正常,但是当我运行组合的可执行文件时,它会抛出这个异常:

未处理的异常:Quartz.SchedulerException:无法实例化 ThreadPool 类型“Quartz.Simpl.SimpleThreadPool”。---> System.InvalidCastException:无法将“Quartz.Simpl.SimpleThreadPool”类型的对象转换为“Quartz.Spi.IThreadPool”类型。
在 Quartz.Util.ObjectUtils.InstantiateType[T](Type type) in :line 0
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
--- End of internal exception stack trace ---
at Quartz.Impl。 StdSchedulerFactory.Instantiate() in :line 0
at Quartz.Impl.StdSchedulerFactory.GetScheduler() in :line 0

有谁知道这是为什么?我已经浪费了4个多小时,我无法弄清楚。如果我不与 ILMerge 结合使用,那么一切都运行良好(Quartz.dll 和 Common.Logging.dll 在同一目录中)。

我敢肯定,以前一定有人尝试过像这样打包 Quartz.net,有什么想法吗?

4

2 回答 2

1

您可以尝试创建自己的 ISchedulerFactory 并避免使用反射来加载所有类型。StdSchedulerFactory 使用此代码创建线程池。这是您的错误发生的地方,也是开始进行更改的地方:

        Type tpType = loadHelper.LoadType(cfg.GetStringProperty(PropertyThreadPoolType)) ?? typeof(SimpleThreadPool);

        try
        {
            tp = ObjectUtils.InstantiateType<IThreadPool>(tpType);
        }
        catch (Exception e)
        {
            initException = new SchedulerException("ThreadPool type '{0}' could not be instantiated.".FormatInvariant(tpType), e);
            throw initException;
        }

调用的 ObjectUtils.InstantiateType 方法就是这个,最后一行是抛出异常的那个:

    public static T InstantiateType<T>(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type", "Cannot instantiate null");
        }
        ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
        if (ci == null)
        {
            throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
        }
        return (T) ci.Invoke(new object[0]);
    }

在工厂的这一部分之后,使用相同的模式加载数据源,然后动态加载作业本身,这意味着您还必须编写自己的 JobFactory。由于 Quartz.Net 在运行时动态加载一堆零碎的东西,这意味着你最终可能会重写大量的东西。

于 2013-05-05T19:30:20.443 回答
1

免责声明:我根本不了解 Quartz.NET,尽管我花了一些时间在 ILMerge 上苦苦挣扎。当我终于了解它的局限性时......我停止使用它。

ILMerge 的应用程序往往对包含“反射”一词的所有内容都有问题。我可以猜测(我从未使用过 Quartz.NET)某些类是使用反射解析并由配置文件驱动的。

类不仅由它的名称(带有命名空间)标识,而且由它来自的程序集标识(不幸的是它没有显示在异常消息中)。因此,假设您(在 ILMerging 之前)有两个程序集 A(对于您的应用程序)和 Q(对于 Quartz.NET)。程序集“A”引用程序集“Q”,并使用了实现“Q:QIntf”的类“Q:QClass”。合并后,这些类变成了“A:QClass”和“A:QIntf”(它们从程序集 Q 移动到 A)并且代码中的所有引用都已被替换为使用那些(完全)新的类/接口,所以“A :QClass" 现在正在实施 "A:QIntf"。但是,它没有更改任何可能仍引用“Q:QClass”的配置文件/嵌入字符串。

因此,当应用程序正在读取那些未更新的配置文件时,它仍会加载“Q:QClass”(为什么它会发现这是一个不同的问题,也许您将程序集“Q”留在当前文件夹中,或者它可能在 GAC 中 - 见 1) . 无论如何,“Q:QClass”没有实现“A:QIntf”,即使它们是二进制相同的,它仍然实现“Q:QIntf”——所以你不能将“Q:QClass”转换为“A:QIntf”。

不理想但可行的解决方案是“嵌入”程序集而不是“合并”它们。我写了一个开源工具来完成它(嵌入而不是合并),但它与这个问题无关。所以如果你决定嵌入就问我。

  1. 您可以通过删除(隐藏,任何对您有用的东西)PC 上的每个 Q.dll 实例来测试它。如果我是对的,异常现在应该说“FileNotFound”。
于 2013-05-07T16:45:47.570 回答