如何在WPF应用程序中注册具有多个接口的服务



我有一个小的WPF应用程序,它使用IServiceCollection在App.xaml.cs文件中注入依赖项。此外,我还创建了一个单一视图模型,负责管理主窗口上的画布。在画布上,有一些按钮可以动态添加命令(我称之为活动(。所有的命令都需要访问视图模型,所以我已经将它注入到活动中。

// App.xaml.cs
private void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<HyperViewModel>();
services.AddSingleton<HideEditor>();
…
}
// ViewModel 
public partial class HyperViewModel : INotifyPropertyChanged, IHyperData, 
IHyperLayout, IHyperContext, IHyperController { ... }
// Example activity 
public class HideEditor : Activity
{
private HyperViewModel _hypermodel;
public void HideEditor(HyperViewModel hypermodel) 
{
_hypermodel = hypermodel;; 
}
// Activity handler
protected override void HandleActivity()
{
// using _hypermodel in here works great
…
}
}

那里的一切都按预期工作,我可以按预期访问活动类中的视图模型。随着我开发的视图模型类越来越大,所以我想将我的依赖关系分离到各种接口中。视图模型类显示了上面示例中的一些接口。我已经将缩小的接口添加到IOC容器中,并将其中一个注入到我的HideEditor活动中。。

// App.xaml.cs
private void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<HyperViewModel>();
services.AddSingleton<IHyperData,HyperViewModel>();
services.AddSingleton<IHyperLayout,HyperViewModel>();
services.AddSingleton<IHyperContext,HyperViewModel>();
services.AddSingleton<IHyperController,HyperViewModel>();
services.AddSingleton<HideEditor>();
…
}
// Example activity 
public class HideEditor : Activity
{
private IHyperLayout _hyperlayout;
public void HideEditor(IHyperLayout hyperlayout) 
{
_hyperlayout = hyperlayout; 
}
// Activity handler
protected override void HandleActivity()
{
// using _hyperlayout in here injects the view model
// but it seems to be a different instance
…
}
}

我希望注入的IHyperLayout接口将允许我通过启动时注入的接口访问视图模型单例。然而,属性不同,而且它似乎是视图模型的一个新的未初始化版本。我很困惑为什么注入的接口没有指向singleton实例。

看起来,我花了所有的时间问这个问题后,几乎立刻就找到了答案。无论如何,它都很简单。每次我将实例添加为singleton时,都会创建这些实例。GetRequiredService委托允许我选择正确的实例。从那时起,我可以通过依赖项注入来访问该实例。

services.AddSingleton<HyperViewModel>();
services.AddSingleton<IHoneycombData>(x => x.GetRequiredService<HoneycombService>());
services.AddSingleton<IHyperContext>(x => x.GetRequiredService<HyperViewModel>());
services.AddSingleton<IHyperController>(x => x.GetRequiredService<HyperViewModel>());

这里有一个链接到一个例子,我在那里找到了答案

如果你想美化语法、提高可读性并增加一些便利性,我可以为你提供以下自定义扩展方法:

用法

明确注册接口:

private void ConfigureServices(ServiceCollection services)
{
services.AddMultiExportSingleton<HyperViewModel>()
.AsService<IHoneycombData>()
.AsService<IHyperContext>()
.AsService<IHyperController>()
.AddSingleton<SomeOtherType>()
.BuildServiceProvider();
}

注册具体类型的所有实现接口:

private void ConfigureServices(ServiceCollection services)
{
services.AddMultiExportSingleton<HyperViewModel>()
.AsImplementedServices()
.AddSingleton<SomeOtherType>()
.BuildServiceProvider();
}

实施

IMultiExportServiceCollection.cs
当前IServiceCollection扩展其功能的外观。

// Use the original .NET namespace for convenience
namespace Microsoft.Extensions.DependencyInjection
{
using System;
public interface IMultiExportServiceCollection : IServiceCollection
{
IMultiExportServiceCollection AsService<TService>() where TService : class;
IServiceCollection AsImplementedServices();
}
}

MultiExportServiceCollection.cs
实现当前IServiceCollection的facade以扩展其功能。

// Use the original .NET namespace for convenience
namespace Microsoft.Extensions.DependencyInjection
{
using System;
using System.Collections;
using System.Collections.Generic;
public class MultiExportServiceCollection : IMultiExportServiceCollection
{
public MultiExportServiceCollection(IServiceCollection serviceCollection, Type tImplementation)
{
this.ServiceCollection = serviceCollection;
this.TImplementation = tImplementation;
}
public IMultiExportServiceCollection AsService<TService>() where TService : class
{
this.ServiceCollection.AddSingleton(typeof(TService), serviceProvider => serviceProvider.GetService(this.TImplementation));
return this;
}
public IServiceCollection AsImplementedServices()
{
Type[] interfaces = this.TImplementation.GetInterfaces();
foreach (Type interfaceType in interfaces)
{
this.ServiceCollection.AddSingleton(interfaceType, serviceProvider => serviceProvider.GetService(this.TImplementation));
}
if (this.TImplementation.BaseType != typeof(object))
{
this.ServiceCollection.AddSingleton(this.TImplementation.BaseType, serviceProvider => serviceProvider.GetRequiredService(this.TImplementation));
}
return this.ServiceCollection;
}
public int IndexOf(ServiceDescriptor item) => this.ServiceCollection.IndexOf(item);
public void Insert(int index, ServiceDescriptor item) => this.ServiceCollection.Insert(index, item);
public void RemoveAt(int index) => this.ServiceCollection.RemoveAt(index);
public void Add(ServiceDescriptor item) => this.ServiceCollection.Add(item);
public void Clear() => this.ServiceCollection.Clear();
public bool Contains(ServiceDescriptor item) => this.ServiceCollection.Contains(item);
public void CopyTo(ServiceDescriptor[] array, int arrayIndex) => this.ServiceCollection.CopyTo(array, arrayIndex);
public bool Remove(ServiceDescriptor item) => this.ServiceCollection.Remove(item);
public IEnumerator<ServiceDescriptor> GetEnumerator() => this.ServiceCollection.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.ServiceCollection.GetEnumerator();
public int Count => this.ServiceCollection.Count;
public bool IsReadOnly => this.ServiceCollection.IsReadOnly;
public ServiceDescriptor this[int index]
{
get => this.ServiceCollection[index];
set => this.ServiceCollection[index] = value;
}
private Type TImplementation { get; }
private IServiceCollection ServiceCollection { get; }
}
}

DependencyInjection.cs
一组扩展方法,用于帮助向IServiceCollection注册服务。

// Use the original .NET namespace for convenience
namespace Microsoft.Extensions.DependencyInjection
{
using System;
using System.Collections;
static class DependencyInjection
{
/// <summary>    /// 
/// Register a service implementation with multiple interfaces. Use <see cref="AsService{TService}(IMultiExportServiceCollection)"/> to attach more service interfaces to the service implementation <typeparamref name="TImplementation"/>
/// or use <see cref="AsImplementedServices(IMultiExportServiceCollection)"/> to register all implemented interfaces of the implementation.
/// </summary>
/// <typeparam name="TImplementation"></typeparam>
/// <param name="serviceCollection"></param>
/// <returns>An <see cref="IMultiExportServiceCollection"/> which implements <see cref="ICollection"/> and aggreagates multiple service interfaces mapped to a single implementation.</returns>
public static IMultiExportServiceCollection AddMultiExportSingleton<TImplementation>(this IServiceCollection serviceCollection) where TImplementation : class
{
serviceCollection.AddSingleton<TImplementation>();
return new MultiExportServiceCollection(serviceCollection, typeof(TImplementation));
}
}
}

最新更新