2013-05-01 2 views
5

Я использую ILMerge и Quartz.NET в приложении C# .NET 4.0 для Windows. Приложение работает отлично, не используя ILMerge, но теперь, когда мы приближаемся к выпуску доставки, я хотел объединить все библиотеки DLL в один исполняемый файл.Исполняемый файл не работает с странным исключением

Проблема заключается в том, что ILMerge, кажется, работает хорошо, но когда я запускаю комбинированную исполняемый, он бросает это исключение:

Необработанное исключение: Quartz.SchedulerException: Тип ThreadPool «Quartz.Simpl.SimpleThreadPool» может не создаваться. ---> System.InvalidCastException: Невозможно наложить объект типа Quartz.Simpl.SimpleThreadPool на тип «Quartz.Spi.IThreadPool».
на Quartz.Util.ObjectUtils.InstantiateType [T] (Тип) в: линия 0
в Quartz.Impl.StdSchedulerFactory.Instantiate() в: линия 0
--- Конец внутренней трассировки стека исключений - -
на Quartz.Impl.StdSchedulerFactory.Instantiate() в: линии 0
в Quartz.Impl.StdSchedulerFactory.GetScheduler() в: линия 0

кто-нибудь есть какие-либо идеи, почему это? Я уже более 4 часов трачу, и я не могу понять. Если я не совмещаюсь с ILMerge, все работает нормально (с Quartz.dll и Common.Logging.dll в том же каталоге).

Я уверен, что кто-то, должно быть, попробовал упаковку Quartz.net, как это раньше, любые идеи?

+0

Это первый раз, когда вы попытались объединить его с ILMerge? Или он работал до недавних изменений? –

+1

Впервые я попытался использовать ILMerge, запустил его, больше не работал. Понял, что это, должно быть, ILMerge, попробовал флаг интернализации, ничего не изменил. Удалите ILMerge, скомпилированный в обычном режиме (например, я использовал это, прежде чем попробовать это), все работает (если библиотеки DLL находятся в одном каталоге). –

+0

Одна из вещей, которые ILMerge не обрабатывает, - это загрузка по типу с внешней сборки (что может иметь место на основе взгляда на stacktrace). Возможно, посмотрите также на одну из альтернатив, найденных здесь (http://chrisghardwick.blogspot.nl/2012/01/ilmerge-getting-started-merging-and.html) – rene

ответ

1

Отказ от ответственности: Я вообще не знаю Quartz.NET, хотя я провел некоторое время с ILMerge. Когда я, наконец, понял его ограничения ... Я прекратил использовать его.

Приложение ILMerge'd имеет проблемы со всем, что содержит слово «отражение». Я могу догадаться (я никогда не использовал Quartz.NET), что некоторые классы решаются с помощью отражения и управляются конфигурационными файлами.

Класс не только идентифицируется по его названию (с пространством имен), но и по сбору сборки (к сожалению, он не отображается в сообщении об исключении). Итак, предположим, что у вас были (до ILMerging) две сборки A (для вас Application) и Q (для Quartz.NET). Сборка «A» ссылалась на сборку «Q» и использовала класс Q: QClass, который реализовывал «Q: QIntf». После слияния эти классы стали «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. Вы можете проверить его, удалив (спрятав, что сработает для вас) каждый экземпляр Q.dll на вашем ПК. Если я прав, исключение должно сказать теперь «FileNotFound».
+0

Я закончил тем, что не использовал ILMERGE, он просто не работал и, казалось, требовал гораздо больше работы, чем пользы от того, что файлы с файлами меньше при развертывании. Я также пытался внедрить, динамически загружать сборки, что помогло мне понять намного больше за кулисами, но это просто не сработало бы для этого. Я загрузил сборки в самый ранний момент, но у них было много статических классов, и они нуждались в их повторной загрузке, но даже статический конструктор был слишком поздно, странно. всегда терпел неудачу. ваш ответ имел больше всего фона, поэтому спасибо! –

+0

Что вы подразумеваете под «загрузкой сборок в самой ранней точке»? Я успешно использовал метод Джеффри Рихтера, описанный в его блоге (см. Мой предыдущий комментарий). Это было очень прямолинейно, и нужно следить только за тем, чтобы убедиться, что событие AssemblyResolve инициализировано раньше всего, что в моем случае означало создание новой точки входа, которая настраивает событие AssemblyResolve, а затем вызывает старую точку входа. – sgmoore

+0

@RomanMittermayr: Я также озадачен «ранней» точкой. Хотя статический конструктор довольно рано, точка ушей - это инициализатор модуля. К сожалению, инициализатор модуля не может быть изменен с помощью C#, но его можно изменить с помощью некоторых небольших манипуляций с IL (см. Https://github.com/Fody/ModuleInit). Когда вы говорите «перезагружен», я чувствую, что Quartz.NET создает отдельные AppDomains. На самом деле это может быть проблемой, если вы не можете получить к ним доступ и ввести в них AssemblyResolver. Вы также можете попробовать https://libz.codeplex.com/. –

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 загружает кучу бит и кусков динамически во время выполнения этой дороги, значит, вы можете переписать много вещей.

Смежные вопросы