«Не удалось найти IPlatformOperations. Это никогда не должно происходить, ваш преобразователь зависимостей не работает» в WPF - PullRequest
0 голосов
/ 25 апреля 2018

Я пишу программу, которая является контейнером для других, небольших программ.Он загружает свои модули через Assembly.Load, находит типы, реализующие IModule, и создает их экземпляры.

В WPF MainWindow у меня есть RoutedViewHost, который будет отображать все.

В моем AppBoostrapper у меня естьследующее:

     private ReactiveList<IModule> LoadModules(IMutableDependencyResolver resolver)
        {
            var modules = ModuleLoader.Load(ModulesDirectory);
//                        var modules = new IModule[] { new SampleModuleClass(), }; // this works perftectly!

            foreach (var module in modules)
            {
                try
                {
                    module.RegisterDependencies(this, resolver);
                }
                catch (Exception e)
                {
                    Log.Error(e, "Could not register dependecies for module " + module.Name);
                }
            }

            Log.Debug("Modules loaded: " + string.Join(", ", modules.Select(x => x.Name)));
            return new ReactiveList<IModule>(modules);
        }

Затем в моем примере модуля:

  public void RegisterDependencies(IMainScreen screen, IMutableDependencyResolver resolver)
        {
            _screen = screen;
            _resolver = resolver;


            resolver.Register(() => new SampleView(), typeof(IViewFor<SampleViewModel>));
            resolver.Register(() => new GetNameDialogView(),  typeof(IViewFor<GetNameDialogViewModel>));

            Log.Debug("Dependecies registered");
        }

Кроме того, у каждого модуля есть свой MainViewModel, который отображается RoutedViewHost при выборе модуля.

К сожалению, это не работает.Я получаю следующую ошибку:

ReactiveUI.RoutedViewHost ОШИБКА Не удалось найти IPlatformOperations.Этого никогда не должно случиться, ваш преобразователь зависимостей не работает

ModuleLoader.Load работает так:

  public static IModule[] Load(string path)
    {
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
            Log.Error("No modules directory found - creating");
            return new IModule[0];
        }

        var moduleTypes = GetTypes(path);

        return moduleTypes.Select(MakeInstance).Where(x => x != null).ToArray();
    }

    private static IModule MakeInstance(Type type)
    {
        try
        {
            var module = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as IModule;

            if (module != null)
            {
                Log.Info("{0} module succesfully instatiated", module.Name);
                return module;
            }

            Log.Error("Could not instantiate {0}", type.FullName);
            return null;
        }
        catch (Exception exception)
        {
            Log.Error(exception, "Exception during instatiating {0}", type.FullName);
            return null;
        }
    }

    private static List<Type> GetTypes(string path)
    {
        var di = new DirectoryInfo(path);

        var moduleTypes = new List<Type>();

        foreach (var dir in di.GetDirectories())
        {
            FileInfo[] files = dir.GetFiles("*.dll");
            foreach (var file in files)
            {
                Assembly newAssembly = Assembly.LoadFile(file.FullName);
                Type[] types = newAssembly.GetExportedTypes();
                foreach (var type in types)
                {
                    if (type.IsClass && !type.IsAbstract && (type.GetInterface(typeof(IModule).FullName) != null))
                    {
                        moduleTypes.Add(type);
                        Log.Debug("Loaded {0} type", type.Name);
                    }
                }
            }
        }
        return moduleTypes;
    }

Нет ошибки, если я просто создаю экземпляр вместо загрузкисборка.Если это важно, SampleModule также использует ReactiveUI.

Я пытался добавить Locator.CurrentMutable.InitializeReactiveUI(); в разных местах (конструктор MainWindow, конструктор приложения, статический конструктор модуля), но ничего не помогло.Есть идеи?

РЕДАКТИРОВАТЬ: если это важно, MainWindow - это MetroWindow от mahapps.metro

EDIT2:

Я пытался зарегистрировать PlatformOperations, как @Wouter предложил для:

`var iPlatformOperations = Type.GetType (" ReactiveUI.IPlatformOperations, ReactiveUI, Версия = 7.4.0.0, Culture = нейтральный, PublicKeyToken = null ");

resolver.Register (() => new PlatformOperations (), iPlatformOperations); `

как до загрузки модуля, так и после, но ничего не меняется.

1 Ответ

0 голосов
/ 05 мая 2018

Что ж, получается, что простой вызов Assembly.Load("ReactiveUI"); ломает все, поэтому я просто добавил фильтрацию загружаемых библиотек DLL, чтобы загружать только те, у которых в имени указано «Модуль».

Тогда я столкнулся с новой проблемой: представление местоположения не работает с динамически загружаемыми сборками, поэтому я создал свой собственный ViewLocator со следующей реализацией ResolveView:

 public IViewFor ResolveView<T>(T viewModel, string contract = null)
            where T : class
        {
            var typeToFind = ViewModelToViewFunc(viewModel.GetType().AssemblyQualifiedName);

            var ret = attemptToResolveView(Reflection.ReallyFindType(typeToFind, false), contract);
            if (ret != null) return ret;

            // IViewFor<FooBarViewModel> (the original behavior in RxUI 3.1)
            var viewType = typeof(IViewFor<>);
            ret = attemptToResolveView(viewType.MakeGenericType(viewModel.GetType()), contract);
            if (ret != null) return ret;


            // check ViewModel's assembly
            var typeAssembly = viewModel.GetType().Assembly;
            var types = typeAssembly.GetExportedTypes()
                .SelectMany(x => x.GetInterfaces()
                .Where(i => i.Name.Contains("IViewFor")))
                .ToArray(); // all IViewFor from assembly

            types = types.Where(x => x.FullName.Contains(viewModel.GetType().Name.Replace("ViewModel", "View"))).ToArray(); // only IViewFor<ViewModel>

            foreach (var type in types) // in case there is more than one registration - contracts
            {
                ret = attemptToResolveView(type, contract);
                if (ret != null) return ret;
            }

            return null;
        }
...