UWP模板10和服务拒绝注入(MVVM)不是WPF

时间:2017-09-07 03:10:26

标签: dependency-injection uwp inversion-of-control uac template10

我花了两周多时间搜索谷歌,bing,堆栈溢出和msdn文档,试图找出如何为我正在开发的移动应用程序进行适当的依赖注入。要清楚,我每天都在网络应用程序中进行DI。我不需要关于什么,谁以及为什么DI很重要的速成课程。我知道它是,并且我总是拥抱它。

我需要了解的是它在移动应用程序世界中的运作方式,尤其是UWP Template 10 Mobile应用程序。

从我的过去,在.net / Asp应用程序中我可以" RegisterType(新XYZ).Singleton()等等#34; {请原谅语法;只是一个例子}在App_Start.ConfigureServices中。这在.netcore中几乎完全相同,授予了一些语法更改。

我的问题是现在我正在尝试提供我的api将要使用需要消化我的IXYZ服务的UWP应用程序。绝不是我认为他们应该"新"每次都有一个实例。必须有一种方法将其注入UWP方面的容器中;而且我觉得我在这个过程中缺少一些非常的东西。

这是我的代码:

App.xaml.cs

public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
    {
        // TODO: add your long-running task here

        //if (args.Kind == ActivationKind.LockScreen)
        //{

        //}
        RegisterServices();
        await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));

    }

public static IServiceProvider Container { get; private set; }

private static void RegisterServices()
{
    var services = new ServiceCollection();
    services.AddSingleton<IXYZ, XYZ>();
    Container = services.BuildServiceProvider();
}

MainPage.xaml.cs中:

public MainPage()
{
   InitializeComponent();
   NavigationCacheMode = NavigationCacheMode.Enabled;
}

MainPageViewModel:

public class MainPageViewModel : ViewModelBase
{
    private readonly IXYZ _xyz;
    public MainPageViewModel(IXYZ xyz)
    {
        //Stuff
        _xyz= xyz;
    }

}

我现在收到错误: XAML MainPage ...无法构造ViewModel类型。为了在XAML中构造,类型不能是抽象的,接口嵌套的泛型或结构,并且必须具有公共默认构造函数。

我愿意使用任何品牌的IoC Container,但我需要的是如何在UWP应用中正确使用DI服务的示例。关于DI的99.9%的问题是关于视图(即Prism?)而不仅仅是服务的简单DI(即DataRepo;又称API / DataService)。

再一次,我觉得我错过了一些明显的东西,需要在正确的方向上轻推。有人可以告诉我一个示例项目,基本代码,或基于我不应该成为程序员的基础......请不要这样做(我不知道我的自我是否可以接受它)。

2 个答案:

答案 0 :(得分:1)

您可以尝试像ASP.NET一样使用Microsoft.Hosting.Extensions,James Montemagno在Xamarin.Forms上有一个实现,它也可以在我尝试过的UWP中使用,并且效果很好。您必须更改某些部分才能使其正常工作。

  

在OnLaunched方法中,添加Startup.Init();

public static class Startup
{
    public static IServiceProvider ServiceProvider { get; set; }
    public static void Init()
    {
        StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
        var configFile = ExtractResource("Sales.Client.appsettings.json", LocalFolder.Path);

        var host = new HostBuilder()
                    .ConfigureHostConfiguration(c =>
                    {
                        // Tell the host configuration where to file the file (this is required for Xamarin apps)
                        c.AddCommandLine(new string[] { $"ContentRoot={LocalFolder.Path}" });

                        //read in the configuration file!
                        c.AddJsonFile(configFile);
                    })
                    .ConfigureServices((c, x) =>
                    {
                        // Configure our local services and access the host configuration
                        ConfigureServices(c, x);
                    }).
                    ConfigureLogging(l => l.AddConsole(o =>
                    {
                        //setup a console logger and disable colors since they don't have any colors in VS
                        o.DisableColors = true;
                    }))
                    .Build();

        //Save our service provider so we can use it later.
        ServiceProvider = host.Services;
    }

    static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
    {
        //ViewModels
        services.AddTransient<HomeViewModel>();
        services.AddTransient<MainPageViewModel>();
    }

    static string ExtractResource(string filename, string location)
    {
        var a = Assembly.GetExecutingAssembly();

        using (var resFilestream = a.GetManifestResourceStream(filename))
        {
            if (resFilestream != null)
            {
                var full = Path.Combine(location, filename);

                using (var stream = File.Create(full))
                {
                    resFilestream.CopyTo(stream);
                }
            }
        }
        return Path.Combine(location, filename);
    }
}

Injecting a ViewModel也可以,这很好。

答案 1 :(得分:0)

在@mvermef和SO问题Dependency Injection using Template 10的帮助下,我找到了解决方案。事实证明这是一个兔子洞,每次转弯都会遇到问题。

第一个问题是让依赖注入工作。一旦我能够从上面的来源中弄明白,我就可以开始将我的服务注入ViewModels并将它们设置为后面代码中的DataContext。

然后我遇到注入问题,将我的IXYZ服务注入到UserControls的ViewModels中。

Pages和他们的ViewModel工作得很好,但我遇到的问题是UserControl的DataContext没有注入UserControl的ViewModel。相反,它们是由持有它的Page的ViewModel注入的。

最终的解决方案是确保 UserControl XAML 中设置了DataContext不是后面的代码,就像我们对Pages做的那样,然后在后面的代码中创建DependencyProperty。

要显示下面的基本解决方案。

为了使它成功,我开始:

<强> APP.XAML.CS

public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
        // long-running startup tasks go here
       RegisterServices();
       await Task.CompletedTask;
}

private static void RegisterServices()
{
        var services = new ServiceCollection();
        services.AddSingleton<IRepository, Repository>();
        services.AddSingleton<IBinderService, BinderServices>();

        **//ViewModels**
        **////User Controls**
        services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
        services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();

        **//ViewModels**
        **////Pages**
        services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
        services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
        etc....

        Container = services.BuildServiceProvider();
}

public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
       **//INJECT THE VIEWMODEL FOR EACH PAGE**
       **//ONLY THE PAGE NOT USERCONTROL**
        if (page is CallListPage)
        {
            return Container.GetService<CallListPageViewModel>();
        }
        if (page is CallListResultPage)
        {
            return Container.GetService<CallListResultPageViewModel>();
        }

        etc...
        return base.ResolveForPage(page, navigationService);
    }

在页面背后的代码中

<强> CALLLISTPAGE.XAML.CS

public CallListPage()
    {
        InitializeComponent();
    }

    CallListPageViewModel _viewModel;

    public CallListPageViewModel ViewModel
    {
        get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
    }

在您的XAML中添加您的UserControl

<强> CALLLISTPAGE.XAML

<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>

在UserControl中,确保将DataContext添加到XAML NOT 后面的代码,就像我们对页面所做的那样。

<强> COMPANYCONTROL.XAML

<UserControl.DataContext>
    <viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>

在UserControl Code Behind中添加依赖属性

<强> COMPANYCONTROL.XAML.CS

    public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
        "Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));

    public CompanyControl()
    {
        InitializeComponent();
    }

    public Company Company
    {
        get => (Company) GetValue(CompanyProperty);
        set => SetValue(CompanyProperty, value);
    }

    private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as CompanyControl;
        var viewModel = control?.ViewModel;
        if (viewModel != null)
            viewModel.Company = (Company) e.NewValue;
    }

最后我不确定这是否是一个优雅的解决方案,但它确实有效。