MVVM Light - PCL+WPF - 获取类型 Microsoft.Practices.ServiceLocation.ActivationException 的异常
MVVM Light - PCL+WPF - Getting exception of type Microsoft.Practices.ServiceLocation.ActivationException
背景
各位 SO 观众大家好。我通常是一名 Android 开发人员,但现在我正在开发一个针对 WPF 和 Android 的跨平台应用程序。话虽如此,实际上没有关于如何直接做我想做的事情的信息。因此,我最终找到了一个由 3 部分组成的博客系列,该系列深入介绍了如何开发基于 Windows 的跨平台 MVVM 项目。只要我将 PCL 设置为与 Xamarin.Android 兼容,任何不抛出错误的代码都应该在我到达 Android 方面时起作用。以下是博客 posts 的链接:Blog 1, Blog 2, Blog 3。同样,我会 Android,所以我是 WPF 应用程序编码的新手。
问题
所以我今天的问题只处理与上述链接博客 post 相关的 PCL-WPF 方面。我尽可能地遵循了 post 中列出的每一个步骤。该博客使用 WinRT 和 WinPhone 作为两个目标平台,因此我不得不尝试自己弄清楚事情以使事情在 WPF 上运行。我必须做的两件事是使用 IsolatedStorage
并基本上使用 WinPhone App.Xaml
来构建 WPF 端。
博客一路写到最后,搭建成功。我什至可以在第三篇博客 post 的末尾看到我的示例调试行。但是,当我转到 运行 时,我得到以下信息:
ActivationException was unhandled by user code
An exception of type 'Microsoft.Practices.ServiceLocation.ActivationException' occurred in GalaSoft.MvvmLight.Extras.dll but was not handled in user code
$exception {Microsoft.Practices.ServiceLocation.ActivationException: Type not found in cache: StackOverF.Services.IStorageService.
at GalaSoft.MvvmLight.Ioc.SimpleIoc.DoGetService(Type serviceType, String key, Boolean cache) in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 537
at GalaSoft.MvvmLight.Ioc.SimpleIoc.GetService(Type serviceType) in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 789
at GalaSoft.MvvmLight.Ioc.SimpleIoc.MakeInstanceTClass in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 729} System.Exception {Microsoft.Practices.ServiceLocation.ActivationException}
你们有什么可以告诉我的,也许博客作者跳过了我需要做的事情吗?也许如果向这个 "boulder," 扔足够多的石头,它就会裂开...
澄清
我的Visual Studio解决方案目前基本上只有两个项目。一个是便携式 Class 库。另一个是 WPF 应用程序。在不久的将来,一旦我在等式的 WPF 方面开始工作,我将使用 Xamarin 中的 PCL 在 Android 项目中重用代码。但是,Android 方面是 而不是 我的问题的一部分。我在处理 WPF 项目时遇到了上述问题。
代码(最后编辑于 2016 年 2 月 18 日)
IMainViewModel.cs
using GalaSoft.MvvmLight.Command;
using StackOverF.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.ViewModels {
public interface IMainViewModel {
ObservableCollection<Workload> Workload { get; }
RelayCommand RefreshCommand { get; }
RelayCommand AddCommand { get; }
}
}
MainViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using StackOverF.Models;
using StackOverF.Services;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.ViewModels {
public class MainViewModel : ViewModelBase,IMainViewModel {
private IDataService dataService;
public MainViewModel(IDataService dataService) {
this.dataService = dataService;
RefreshAsync();
}
private ObservableCollection<Workload> workload = new ObservableCollection<Workload>();
public ObservableCollection<Workload> Workload {
get {
return workload;
}
}
#region Commands
#region Refresh
private RelayCommand refreshCommand;
public RelayCommand RefreshCommand {
get {
return refreshCommand ?? (refreshCommand = new RelayCommand(async () => { await RefreshAsync();}));
}
}
private async Task RefreshAsync() {
workload.Clear();
foreach (Workload listing in await dataService.GetWorkloadAsync()) {
workload.Add(listing);
}
}
#endregion
#region Add
private RelayCommand addCommand;
public RelayCommand AddCommand {
get {
return addCommand ??
(addCommand = new RelayCommand(async () => {
Workload listing = new Workload() { Id = 3, Serial = "relay12" };
await dataService.AddWorkloadAsync(listing);
workload.Add(listing);
}));
}
}
#endregion
#endregion
}
}
LocatorService.cs(DeviceLocatorService,位于WPF项目中)
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.Services {
public class DeviceLocatorService {
static DeviceLocatorService() {
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic) {
}
else {
}
if (!SimpleIoc.Default.IsRegistered<IStorageService>())
SimpleIoc.Default.Register<IStorageService, StorageService>();
}
public static void Cleanup() {
}
}
}
LocatorService.cs(LocatorService,位于PCL项目中)
using Microsoft.Practices.ServiceLocation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using StackOverF.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.Services {
public class LocatorService {
static LocatorService() {
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Services
if (ViewModelBase.IsInDesignModeStatic) {
SimpleIoc.Default.Register<IDataService, Design.DataService>();
}
else {
SimpleIoc.Default.Register<IDataService, Services.DataService>();
}
// View Models
SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
}
public IMainViewModel MainViewModel {
get {
return ServiceLocator.Current.GetInstance<IMainViewModel>();
}
}
public static void Cleanup() {
}
}
}
它在 return ServiceLocator.Current.GetInstance<IMainViewModel>();
行出错(仅调试时)。
App.xaml
<Application x:Class="StackOverF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:services="clr-namespace:StackOverF.Services;assembly=StackOverF.PCL"
xmlns:deviceServices="clr-namespace:StackOverF.Services"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<deviceServices:DeviceLocatorService x:Key="Locator.WPF" d:IsDataSource="True" />
<services:LocatorService x:Key="Locator" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
据我所知,IoC 容器正在尝试解析 MainWindow
并使其容器解析 IDataService
。但是 IDataService
具有未在 IoC 容器中注册的依赖项 IStorageService
。
在我看来,IStorageService
未注册,因为从未调用 DeviceLocatorService
构造函数。
我认为你对 app.xaml
有疑问
<Application
x:Class="SampleApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ignore="http://www.ignore.com"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d ignore"
xmlns:services="using:SampleApp.Services"
xmlns:local="using:SampleApp">
<Application.Resources>
<ResourceDictionary>
<services:LocatorService x:Key="Locator" d:IsDataSource="True" />
<services:DeviceLocatorService x:Key="Locator.W8" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
我的问题的解决方案比人们想象的要简单。 WPF
应用程序使用 MVVM
toolkits/frameworks 没有问题,但它们似乎在共享方面存在问题。由于 WPF
并非旨在成为一种跨平台友好的语言,因此 "correct" 编写此类程序的方法对它不起作用。
问题出现在尝试将 LocatorService
class 都包含在 App.xaml
中并期望 WPF
到 运行 都 class像 WinRT
或 WinPhone
这样的可能。如果需要数据,WPF
似乎只引用 class。就像在博客的示例中一样,我将 Main.xaml
数据绑定到 LocatorService
class。由于 WPF
应用程序仅 运行 class 的代码,因此它会抛出错误。
解决方案是在 WPF
项目端将 LocatorService
文件合并为一个文件。为什么在 WPF
那边问?一个Portable Class Library
应该只包含通用代码,可跨平台共享。
背景
各位 SO 观众大家好。我通常是一名 Android 开发人员,但现在我正在开发一个针对 WPF 和 Android 的跨平台应用程序。话虽如此,实际上没有关于如何直接做我想做的事情的信息。因此,我最终找到了一个由 3 部分组成的博客系列,该系列深入介绍了如何开发基于 Windows 的跨平台 MVVM 项目。只要我将 PCL 设置为与 Xamarin.Android 兼容,任何不抛出错误的代码都应该在我到达 Android 方面时起作用。以下是博客 posts 的链接:Blog 1, Blog 2, Blog 3。同样,我会 Android,所以我是 WPF 应用程序编码的新手。
问题
所以我今天的问题只处理与上述链接博客 post 相关的 PCL-WPF 方面。我尽可能地遵循了 post 中列出的每一个步骤。该博客使用 WinRT 和 WinPhone 作为两个目标平台,因此我不得不尝试自己弄清楚事情以使事情在 WPF 上运行。我必须做的两件事是使用 IsolatedStorage
并基本上使用 WinPhone App.Xaml
来构建 WPF 端。
博客一路写到最后,搭建成功。我什至可以在第三篇博客 post 的末尾看到我的示例调试行。但是,当我转到 运行 时,我得到以下信息:
ActivationException was unhandled by user code
An exception of type 'Microsoft.Practices.ServiceLocation.ActivationException' occurred in GalaSoft.MvvmLight.Extras.dll but was not handled in user code
$exception {Microsoft.Practices.ServiceLocation.ActivationException: Type not found in cache: StackOverF.Services.IStorageService. at GalaSoft.MvvmLight.Ioc.SimpleIoc.DoGetService(Type serviceType, String key, Boolean cache) in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 537 at GalaSoft.MvvmLight.Ioc.SimpleIoc.GetService(Type serviceType) in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 789 at GalaSoft.MvvmLight.Ioc.SimpleIoc.MakeInstanceTClass in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 729} System.Exception {Microsoft.Practices.ServiceLocation.ActivationException}
你们有什么可以告诉我的,也许博客作者跳过了我需要做的事情吗?也许如果向这个 "boulder," 扔足够多的石头,它就会裂开...
澄清
我的Visual Studio解决方案目前基本上只有两个项目。一个是便携式 Class 库。另一个是 WPF 应用程序。在不久的将来,一旦我在等式的 WPF 方面开始工作,我将使用 Xamarin 中的 PCL 在 Android 项目中重用代码。但是,Android 方面是 而不是 我的问题的一部分。我在处理 WPF 项目时遇到了上述问题。
代码(最后编辑于 2016 年 2 月 18 日)
IMainViewModel.cs
using GalaSoft.MvvmLight.Command;
using StackOverF.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.ViewModels {
public interface IMainViewModel {
ObservableCollection<Workload> Workload { get; }
RelayCommand RefreshCommand { get; }
RelayCommand AddCommand { get; }
}
}
MainViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using StackOverF.Models;
using StackOverF.Services;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.ViewModels {
public class MainViewModel : ViewModelBase,IMainViewModel {
private IDataService dataService;
public MainViewModel(IDataService dataService) {
this.dataService = dataService;
RefreshAsync();
}
private ObservableCollection<Workload> workload = new ObservableCollection<Workload>();
public ObservableCollection<Workload> Workload {
get {
return workload;
}
}
#region Commands
#region Refresh
private RelayCommand refreshCommand;
public RelayCommand RefreshCommand {
get {
return refreshCommand ?? (refreshCommand = new RelayCommand(async () => { await RefreshAsync();}));
}
}
private async Task RefreshAsync() {
workload.Clear();
foreach (Workload listing in await dataService.GetWorkloadAsync()) {
workload.Add(listing);
}
}
#endregion
#region Add
private RelayCommand addCommand;
public RelayCommand AddCommand {
get {
return addCommand ??
(addCommand = new RelayCommand(async () => {
Workload listing = new Workload() { Id = 3, Serial = "relay12" };
await dataService.AddWorkloadAsync(listing);
workload.Add(listing);
}));
}
}
#endregion
#endregion
}
}
LocatorService.cs(DeviceLocatorService,位于WPF项目中)
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.Services {
public class DeviceLocatorService {
static DeviceLocatorService() {
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic) {
}
else {
}
if (!SimpleIoc.Default.IsRegistered<IStorageService>())
SimpleIoc.Default.Register<IStorageService, StorageService>();
}
public static void Cleanup() {
}
}
}
LocatorService.cs(LocatorService,位于PCL项目中)
using Microsoft.Practices.ServiceLocation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using StackOverF.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverF.Services {
public class LocatorService {
static LocatorService() {
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Services
if (ViewModelBase.IsInDesignModeStatic) {
SimpleIoc.Default.Register<IDataService, Design.DataService>();
}
else {
SimpleIoc.Default.Register<IDataService, Services.DataService>();
}
// View Models
SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
}
public IMainViewModel MainViewModel {
get {
return ServiceLocator.Current.GetInstance<IMainViewModel>();
}
}
public static void Cleanup() {
}
}
}
它在 return ServiceLocator.Current.GetInstance<IMainViewModel>();
行出错(仅调试时)。
App.xaml
<Application x:Class="StackOverF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:services="clr-namespace:StackOverF.Services;assembly=StackOverF.PCL"
xmlns:deviceServices="clr-namespace:StackOverF.Services"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<deviceServices:DeviceLocatorService x:Key="Locator.WPF" d:IsDataSource="True" />
<services:LocatorService x:Key="Locator" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
据我所知,IoC 容器正在尝试解析 MainWindow
并使其容器解析 IDataService
。但是 IDataService
具有未在 IoC 容器中注册的依赖项 IStorageService
。
在我看来,IStorageService
未注册,因为从未调用 DeviceLocatorService
构造函数。
我认为你对 app.xaml
<Application
x:Class="SampleApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ignore="http://www.ignore.com"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d ignore"
xmlns:services="using:SampleApp.Services"
xmlns:local="using:SampleApp">
<Application.Resources>
<ResourceDictionary>
<services:LocatorService x:Key="Locator" d:IsDataSource="True" />
<services:DeviceLocatorService x:Key="Locator.W8" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
我的问题的解决方案比人们想象的要简单。 WPF
应用程序使用 MVVM
toolkits/frameworks 没有问题,但它们似乎在共享方面存在问题。由于 WPF
并非旨在成为一种跨平台友好的语言,因此 "correct" 编写此类程序的方法对它不起作用。
问题出现在尝试将 LocatorService
class 都包含在 App.xaml
中并期望 WPF
到 运行 都 class像 WinRT
或 WinPhone
这样的可能。如果需要数据,WPF
似乎只引用 class。就像在博客的示例中一样,我将 Main.xaml
数据绑定到 LocatorService
class。由于 WPF
应用程序仅 运行 class 的代码,因此它会抛出错误。
解决方案是在 WPF
项目端将 LocatorService
文件合并为一个文件。为什么在 WPF
那边问?一个Portable Class Library
应该只包含通用代码,可跨平台共享。