Как избежать загрузки нескольких версий сборки в один и тот же контекст

#c# #.net #reflection #dll #assemblies

#c# #.net #отражение #dll #сборки

Вопрос:

Тема как бы охватывает мою проблему. Я видел документ о наилучшей практике по этой проблеме, но на самом деле он не описывает, как выйти из ситуации. http://msdn.microsoft.com/en-us/library/dd153782.aspx

Моя ситуация. Путь A: основное приложение. (Подписанный, версионный. .net 3.5) Сборка общего интерфейса X. (подписанный, версионный)

Путь B: Пользовательская сборка. Сборка общего интерфейса X. (подписанная, версионная) (потенциально другая версия, чем указано выше. Возможно, вы не знаете фактическую версию, поскольку мы не можем контролировать, что делает клиент / пользователь.)

Наше основное приложение загружает указанный класс из пользовательской сборки, используя отражение, и взаимодействует с классом, используя указанный интерфейс, определенный в сборке общего интерфейса X. Приложения должны оставаться в одном конкретном пути, а пользовательские (бизнес) сборки — в другом пути. Не просите меня изменить это, это не в моей власти.

Проблема в том, что, скорее всего, в пути B. будет копия сборки общего интерфейса X. Поэтому, когда я загружаю пользовательскую сборку с использованием отражения, она автоматически загружает сборку общего интерфейса X с того же пути, вместо того, чтобы использовать тот, который уже загружен в память с приложением. В итоге я получил тип iXXX, который отличается от типа iXXX, или не могу найти конструктор для пользовательского класса Assembly, потому что, опять же, он запутал интерфейсы между двумя копиями Common Interface Assembly X.

Итак, как я могу выполнить загрузку и создать класс из пользовательской сборки по пути B, но не загружать сборку общего интерфейса X из пути B (и использовать сборку из пути A, уже имеющуюся в памяти)?

Дополнительная информация: Большинство проблем, с которыми я сталкиваюсь, возникают вокруг CreateInstance, поскольку ClientContext находится в сборке общего интерфейса X, поэтому, хотя я создал один из пути A, конструктор в моем диалоговом окне, похоже, ожидает один из пути B. Затем я получаю «Конструктор по типу «ExternalTestForms.WindowDemo1’не найден.» ошибка. (Даже с точно такой же [подписанной и версионной] сборкой как по пути A, так и по пути B, эта ошибка все еще возникает.)

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

         public IExternalForm ShowExternalWindow(string filename, string classname, string parameters, string formIdentity)
        {
            if (File.Exists(filename))
            {
                ClientContext context = GetCurrentClientContext(parameters, formIdentity);

                object dialog = null;

                AppDomain currentDomain = AppDomain.CurrentDomain;
                currentDomain.AssemblyResolve  = this.CurrentDomain_AssemblyResolve;

                this.basePath = Path.GetDirectoryName(filename);
                Assembly externalAssembly = Assembly.LoadFile(filename);

                try
                {
                    // The Window must must have a ClientContext defined in the constructor.
                    dialog = externalAssembly.CreateInstance(classname, false, BindingFlags.CreateInstance, null, new object[] { context }, context.Desktop.Culture, null);
                }
                catch (Exception ex)
                {
                    // MessageBox "Could not launch the Window"
                    // LogTraceException
                }

                if (dialog != null amp;amp; dialog is IExternalForm amp;amp; dialog is System.Windows.Window)
                {
                    System.Windows.Window window = (System.Windows.Window)dialog;
...
...
...
                }
...
            }
        }


        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string filename = args.Name.Substring(0, args.Name.IndexOf(","))   ".dll";

            // Seek out the specified assembly in the current Application path first.
            string assemblyPath = Path.Combine(Application.StartupPath, filename);

            if (!File.Exists(assemblyPath))
            {
                assemblyPath = Path.Combine(this.basePath, filename);
            }

            if (File.Exists(assemblyPath))
            {
                // Load the assembly from the specified path.
                Assembly myAssembly = Assembly.LoadFrom(assemblyPath);

                // Return the loaded assembly.
                return myAssembly;
            }

            return null;
        }
  

Комментарии:

1. Не знаю, поможет ли это, но, возможно, привязка, перенаправляющая старую версию на новую версию, которая, вероятно, войдет в конфигурацию основного приложения, поскольку конфигурации для DLL обычно игнорируются: devx.com/tips/Tip/35310

2. не могли бы вы добавить немного кода, чтобы было понятно, как именно вы загружаете сборку?

3. Никогда не используйте LoadFile. Вместо этого используйте LoadFrom().

4. Мы использовали LoadFrom(), но это не имело никакого значения.

Ответ №1:

Рассмотрим несколько различных вариантов с низким уровнем воздействия:

1) При запуске приложения скопируйте сборки клиентов только в основную папку (мы требуем, чтобы наши сборки клиентов имели определенное соглашение об именовании для загрузки, поэтому нам легко реализовать).

2) При запуске приложения удалите все копии стандартных библиотек DLL в каталогах клиентов, чтобы предотвратить конфликты.

Комментарии:

1. Как я уже говорил выше. «Приложения должны оставаться на одном определенном пути, а пользовательские (бизнес) сборки — на другом пути». Это не позволяет выполнить пункт 1. Однако я рассматривал возможность удаления сборки интерфейса с пути B. Это действительно решает проблемы и позволяет избежать попыток использовать обе сборки одновременно (также получал «Внешний псевдоним» «не был указан в параметре / reference», потому что мы используем WPF). Дальнейшее тестирование помогло (ИМО глупо удалять конфликтующий файл с пути пользователя, но это работает. В конечном итоге это все, что имеет значение.)