我知道这个问题是两年前提出的,但是我可能有一个解决方案可以匹配你所要求的。
在过去的几个月里,我一直在使用Xamarin和WPF开发应用程序,并使用了Microsoft.Extensions.DependencyInjection
软件包将构造函数依赖注入到我的视图模型中,就像在ASP.NET控制器中一样。这意味着我可以像下面这样使用:
public class MainViewModel : ViewModelBase
{
private readonly INavigationService _navigationService;
private readonly ILocalDatabase _database;
public MainViewModel(INavigationService navigationService, ILocalDatabase database)
{
_navigationService = navigationService;
_database = database;
}
}
为了实现这种过程,我使用
IServiceCollection
来添加服务和
IServiceProvider
来检索已注册的服务。
重要的是要记住,
IServiceCollection
是您将注册依赖项的容器。然后,在构建此容器时,您将获得一个
IServiceProvider
,它将允许您检索服务。
为此,我通常创建一个名为
Bootstrapper
的类,该类将配置服务并初始化应用程序的主页面。
基本实现
此示例显示如何将依赖项注入到Xamarin页面中。对于任何其他类(ViewModel或其他类),流程都是相同的。
在项目中创建一个简单的名为
Bootstrapper
的类,并初始化一个
IServiceCollection
和
IServiceProvider
私有字段。
public class Bootstrapper
{
private readonly Application _app;
private IServiceCollection _services;
private IServiceProvider _serviceProvider;
public Bootstrapper(Application app)
{
_app = app;
}
public void Start()
{
ConfigureServices();
}
private void ConfigureServices()
{
_services = new ServiceCollection();
_serviceProvider = _services.BuildServiceProvider();
}
}
在
ConfigureServices()
方法中,我们创建了一个新的
ServiceCollection
用来添加我们的服务。(参见
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollection?view=dotnet-plat-ext-3.1)一旦我们添加完成,我们就会构建服务提供程序,以便我们可以检索之前注册的服务。
然后,在您的
App
类构造函数中,创建一个新的
Bootstrapper
实例,并调用start方法初始化应用程序。
public partial class App : Application
{
public App()
{
InitializeComponent();
var bootstrapper = new Bootstrapper(this);
bootstrapper.Start();
}
...
}
使用这段代码,您已经设置了服务容器,但是我们仍然需要初始化应用程序的
MainPage
。返回启动程序的
Start()
方法,并创建所需主页面的新实例。
public class Bootstrapper
{
...
public void Start()
{
ConfigureServices();
var mainPageInstance = ActivatorUtilities.CreateInstance<MainPage>(_serviceProvider);
_app.MainPage = new NavigationPage(mainPageInstance);
}
}
在这里,我们使用
ActivatorUtilities.CreateInstance<TInstance>()
方法创建一个新的
MainPage
实例。我们将
_serviceProvider
作为参数传递,因为
ActivatorUtilities.CreateInstance()
方法将负责创建您的实例并将所需的服务注入到您的对象中。
请注意,这是ASP.NET Core用于使用构造函数依赖项注入实例化控制器的方法。
要测试这个功能,请创建一个简单的服务并尝试将其注入到您的
MainPage
构造函数中:
public interface IMySimpleService
{
void WriteMessage(string message);
}
public class MySimpleService : IMySimpleService
{
public void WriteMessage(string message)
{
Debug.WriteLine(message);
}
}
然后在Bootstrapper
类的ConfigureServices()
方法中注册它:
private void ConfigureServices()
{
_services = new ServiceCollection();
_services.AddSingleton<IMySimpleService, MySimpleService>();
_serviceProvider = _services.BuildServiceProvider();
}
最后,在您的
MainPage.xaml.cs
中,注入
IMySimpleService
并调用
WriteMessage()
方法。
public partial class MainPage : ContentPage
{
public MainPage(IMySimpleService mySimpleService)
{
mySimpleService.WriteMessage("Hello world!");
}
}
你已成功注册服务并将其注入到页面中。
使用 ActivatorUtilities.CreateInstance<T>()
方法,通过传递一个服务提供程序,构造函数注入的真正魔力实际上发生在这里。该方法将检查您的构造函数参数,并尝试通过从您给定的 IServiceProvider
中获取它们来解析依赖项。
奖励:注册特定于平台的服务
很棒吧?由于 ActivatorUtilities.CreateInstance<T>()
方法,您可以将服务注入到任何类中,但有时您还需要注册一些特定于平台的服务(Android 或 iOS)。
使用先前的方法无法注册特定于平台的服务,因为 IServiceCollection
是在 Bootstrapper
类中初始化的。不用担心,解决方法非常简单。
您只需将IServiceCollection
的初始化提取到特定于平台的代码中。只需在Android项目的MainActivity.cs
和iOS项目的AppDelegate
上初始化服务集合,并将其传递给您的App
类,该类将将其转发到Bootstrapper
:
MainActivity.cs(Android)
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
...
var serviceCollection = new ServiceCollection();
var application = new App(serviceCollection);
LoadApplication(application);
}
...
}
AppDelegate.cs (iOS)
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
var serviceCollection = new ServiceCollection();
var application = new App(serviceCollection);
LoadApplication(application);
return base.FinishedLaunching(app, options);
}
}
App.xaml.cs (Common)
public partial class App : Application
{
public App(IServiceCollection services)
{
InitializeComponent();
var bootstrapper = new Bootstrapper(this, services);
bootstrapper.Start();
}
...
}
Bootstrapper.cs(通用)
public class Bootstrapper
{
private readonly Application _app;
private readonly IServiceCollection _services;
private IServiceProvider _serviceProvider;
public Bootstrapper(Application app, IServiceCollection services)
{
_app = app;
_services = services;
}
public void Start()
{
ConfigureServices();
var mainPageInstance = ActivatorUtilities.CreateInstance<MainPage>(_serviceProvider);
_app.MainPage = new NavigationPage(mainPageInstance);
}
private void ConfigureServices()
{
_serviceCollection.AddSingleton<IMySimpleService, MySimpleService>();
_serviceProvider = _services.BuildServiceProvider();
}
}
现在,您可以轻松地注册特定于平台的服务并将接口注入到您的页面/视图模型/类中。
Microsoft.Extensions.DependencyInjection
是一个独立的模块。虽然它作为ASP.Net-Core中的开箱即用的DI容器使用,但它也可以在任何其他支持它的解决方案中单独使用。我相信它应该能够在Xamarin中使用。但请注意,内置的服务容器旨在为框架和大多数基于它构建的消费者应用程序提供基本需求的服务。 - NkosiContainerBuilder
。应该可以很容易地在Nuget上找到它。 - Nkosi