Options是微軟提供的選項模塊,該模塊依賴於容器使用。除了微軟的IServiceCollection,當然也可以使用其它的依賴註入容器。本文演示如何在prism中使用Options。 創建應用項目 創建一個Avalonia應用(或其它類型應用),然後使用NuGet包管理器添加Prism.DryI ...
Options是微軟提供的選項模塊,該模塊依賴於容器使用。除了微軟的IServiceCollection,當然也可以使用其它的依賴註入容器。本文演示如何在prism中使用Options。
創建應用項目
創建一個Avalonia應用(或其它類型應用),然後使用NuGet包管理器添加Prism.DryIoc.Avalonia包。創建Views和ViewModels文件夾,將MainWindow移動到Views文件夾中(註意修改namespace),在ViewModels文件夾中創建MainWindowViewModel,以便Prism自動綁定ViewModel。
public partial class App : PrismApplication
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
base.Initialize();
}
protected override AvaloniaObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<MainWindow>();
}
}
添加Options功能
-
首先使用NuGet添加Microsoft.Extentions.Options和Microsoft.Extensions.Options.ConfigurationExtensions。使用json配置文件進行測試,因此再添加上Microsoft.Extensions.Configuration.Json及Microsoft.Extensions.Configuration.Binder。
-
添加Options靜態類,提供DefaultName:
public static class Options
{
public static readonly string DefaultName = string.Empty;
internal const DynamicallyAccessedMemberTypes DynamicallyAccessedMembers =
DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;
}
- 由於UnnamedOptionsManager為內部類,無法直接使用,因此添加一個UnnamedOptionsManager類,實現IOptions<>介面,直接拷貝源碼即可:
public class UnnamedOptionsManager<[DynamicallyAccessedMembers(Options.DynamicallyAccessedMembers)] TOptions> :
IOptions<TOptions>
where TOptions : class
{
private readonly IOptionsFactory<TOptions> _factory;
private volatile object _syncObj;
private volatile TOptions _value;
public UnnamedOptionsManager(IOptionsFactory<TOptions> factory) => _factory = factory;
public TOptions Value
{
get
{
if (_value is TOptions value)
return value;
lock (_syncObj ?? Interlocked.CompareExchange(ref _syncObj, new object(), null) ?? _syncObj)
{
return _value ??= _factory.Create(Options.DefaultName);
}
}
}
}
- 添加OptionsPrismExtensions擴展類,添加AddOptions擴展方法,將選項泛型介面、工廠、緩存註冊到容器中。工廠註冊為瞬時,其它註冊為單例。客戶端不需要添加IOptionsSnapshot,只添加IOptions<>和IOptionsMonitor<>即可,前者獲取選項不會監聽修改,後者可以監聽選項修改:
public static class OptionsPrismExtensions
{
public static IContainerExtension AddOptions(this IContainerExtension container)
{
ArgumentNullException.ThrowIfNull(container, nameof(container));
container.RegisterSingleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>));
container.RegisterSingleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>));
container.Register(typeof(IOptionsFactory<>), typeof(OptionsFactory<>));
container.RegisterSingleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>));
return container;
}
}
- 添加OptionsConfigurationPrismExtensions擴展類,提供Configuration相關的擴展方法,可以直接將選項和配置Section進行綁定:
public static class OptionsConfigurationPrismExtensions
{
public static IContainerExtension Configure<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.All)] TOptions>(this IContainerExtension container,
IConfiguration config) where TOptions : class
=> container.Configure<TOptions>(Options.DefaultName, config, _ => { });
public static IContainerExtension Configure<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.All)] TOptions>(this IContainerExtension container,
string name, IConfiguration config, Action<BinderOptions> configureBinder)
where TOptions : class
{
ArgumentNullException.ThrowIfNull(container, nameof(container));
ArgumentNullException.ThrowIfNull(config, nameof(config));
container.AddOptions();
container.RegisterInstance<IOptionsChangeTokenSource<TOptions>>(
new ConfigurationChangeTokenSource<TOptions>(name, config));
container.RegisterInstance<IConfigureOptions<TOptions>>(
new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder));
return container;
}
}
使用
- 添加一個settings.json配置文件,設置屬性複製到輸出目錄:如果較新則複製:
{
"Test": {
"Name": "louzi",
"Age": 18,
"Sex": "Male"
}
}
- 添加Test對應的Option實體:
public class TestOption
{
public string Name { get; set; }
public int Age { get; set; }
public Gender Sex { get; set; }
}
public enum Gender
{
Male,
Female
}
- 創建IConfiguration並綁定到選項:
// App
protected override IContainerExtension CreateContainerExtension()
{
var container = base.CreateContainerExtension();
IConfiguration config = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("settings.json", optional: true, reloadOnChange: true).Build();
container.AddOptions().Configure<TestOption>(config.GetSection("Test"));
return container;
}
- ViewModel中通過依賴註入獲取選項:
public class MainWindowViewModel : BindableBase
{
private string _name;
private int _age;
private Gender _sex;
public MainWindowViewModel(IOptions<TestOption> options)
{
var testOption = options.Value;
_name = testOption.Name;
_age = testOption.Age;
_sex = testOption.Sex;
}
public string Name { get => _name; set => SetProperty(ref _name, value); }
public int Age { get => _age; set => SetProperty(ref _age, value); }
public Gender Sex { get => _sex; set => SetProperty(ref _sex, value); }
}
- View中顯示
<Grid RowDefinitions="1*,1*,1*" ColumnDefinitions="1*,1*">
<TextBlock Text="Name: " TextAlignment="Right"/>
<TextBlock Text="{Binding Name}" Grid.Column="1"/>
<TextBlock Text="Age: " Grid.Row="1" TextAlignment="Right"/>
<TextBlock Text="{Binding Age}" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="Sex: " Grid.Row="2" TextAlignment="Right"/>
<TextBlock Text="{Binding Sex}" Grid.Row="2" Grid.Column="1"/>
</Grid>
項目結構及運行效果如下圖: