`Компонент не имеет ресурса, идентифицируемого по URI`, когда сборка находится в другой папке - PullRequest
0 голосов
/ 30 октября 2018

Я создаю модульное приложение WPF с использованием библиотек Prism + Unity. Я разделил свои решения на следующие проекты: - общий проект (интерфейсы) - главное приложение, которое загружает dll - модули, на которые НЕ ссылаются основные проекты приложений.

Подмодули dll собраны в папку bin \ (Debug | Release) \ Modules

Код в главном приложении для загрузки следующий:

Она:

  1. загружает dll из папки Modules
  2. типы регистров в контейнере DI
  3. установка модулей из загруженного dll (поиск типов IModule)

    protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
    {
        base.ConfigureModuleCatalog(moduleCatalog);
    
        List<Assembly> allAssemblies = new List<Assembly>();
        string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    
        foreach (string dll in Directory.GetFiles(path + "/Modules/", "*.dll"))
            allAssemblies.Add(Assembly.LoadFile(dll));
    
        AutoRegisterClasses(allAssemblies);
    
        var modules = AllClasses.FromAssemblies(allAssemblies).Where(t => t.GetInterfaces().Contains(typeof(IModule)));
    
        foreach (var module in modules)
        {
            moduleCatalog.AddModule(
                new ModuleInfo()
                {
                    ModuleName = module.Name,
                    ModuleType = module.AssemblyQualifiedName,
                    Ref = "file://" + module.Assembly.Location
                });
        }
    }
    
    private void AutoRegisterClasses(List<Assembly> allAssemblies)
    {
        var defaultRegisters = AllClasses.FromAssemblies(allAssemblies).Where(t => t.IsDefined(typeof(AutoRegisterAttribute), true));
    
        foreach (var register in defaultRegisters)         
            foreach (var interface_ in register.GetInterfaces()) 
                Container.GetContainer().RegisterType(interface_, register, null, new TransientLifetimeManager());
    }
    

Регистрация и инициализация модуля работает - все называется. Когда я пытаюсь разрешить взаимодействие из общего проекта , который реализован в проекте подмодуля , я получаю правильный объект. Но когда я пытаюсь отобразить ContentControl , который возвращается этим провайдером, я получаю следующее исключение:

Компонент «AdditionalDll.MainWindow» не имеет ресурса, идентифицируемого по URI '/AdditionalDll;component/mainwindow.xaml'.

Понятия не имею, почему это проблема, что более интересно, когда я не храню DLL в папке Modules - то есть, когда я храню их рядом с main exe, все работает !!!

Я создал минимальное решение, которое имеет эту проблему: https://github.com/BAndysc/AssemblyLoadProblemExample

Common/Interfaces.cs - is common project, contains AutoRegister attribute and example interface that I will try to resolve
AdditionalDll/ExampleProvider.cs - is in submodule project, it is implementation of the mentioned interface, it returns new view from AdditionalDll project
TestMultiProjectApp/App.xaml.cs - setups everything - load dlls, register types, init modules
TestMultiProjectApp/MainWindow.xaml - is simple view, which shows given ContentControl
TestMultiProjectApp/MainWindowViewModel.cs - is simple viewmodel, which resolves IProvider interface (it works) and on command assigns returned view to property, which is then displayed by view (this causes exception)

Несмотря на то, что хранить dll в той же папке, что и exe, - это обходной путь, я бы предпочел хранить их в отдельной папке, чтобы сохранить порядок, если у кого-то есть идеи, как их исправить, я буду признателен за это.

1 Ответ

0 голосов
/ 30 октября 2018

Это ошибка на многих уровнях. Например:

  • модуль не найден в каталоге модулей
  • приложение хочет вручную загрузить типы в модулях
  • MainWindowViewModel пытается создать и сохранить ContentControl
  • наименование - почему существует два MainWindow с?

Вы должны сделать шаг назад и пересмотреть свою архитектуру. Каковы обязанности ваших модулей? Какова ответственность вашего приложения? Как вы хотите идентифицировать и загрузить модули? Как вы хотите развернуть и загрузить зависимости модулей? Как запланированный макет вашего графического интерфейса?

И вам следует сделать еще один шаг назад и вернуться к некоторой базовой документации о разработке WPF, а именно к привязке данных, шаблонам данных, словарям ресурсов, разнице между представлением и моделью представления.

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

  • Самым смыслом работы модуля является то, что он регистрирует свои собственные элементы. Так что да, плохо, если кто-то еще делает это для модуля, потому что вы забираете все, что модуль вводит в первую очередь. На самом деле, я бы сказал, что модуль не должен даже предоставлять какие-либо типы public.
  • свойства модели представления либо встроены в типы, либо в другие модели представления. Сопоставление между представлением и моделью представления происходит через DataTemplate s или (в случае Prism) через ViewModelLocator. Никогда не следует, чтобы модель представления владела, создавала или знала тип представления, если вы можете помочь ему.
  • Нет ничего плохого в регистрации согласно соглашению как таковой, но ограничьте себя модулем под рукой. Приложение может зарегистрировать типы, известные приложению, но типы в модулях должны быть зарегистрированы модулями.
  • Prism предоставляет способ создания зависимостей между модулями для определения частичного упорядочения, используемого при загрузке модулей.
...