我希望能够在 MAUI 中注册服务,这样我就能够在实例化时传递给它们的构造函数的参数。
我在想这样的事情:
//register service that requires 2 parameters upon instantiation
builder.Services.AddTransient<IMyService, MyService>(parameterType1, parameterType2);
//get an instance of the service that requires 2 parameters
ServiceHelper.GetService<IMyService>(parameter1, parameter2);
您可以使用 lambda 表达式重载作为依赖项注入的一部分来创建具有不同值的参数化对象。
// Dependency injection in your startup configs
builder.Services.AddTransient<IMyService, MyService>(() =>
{
//ToDo: You can pull your parameter values here, either from the config or constant
// var parameter1 = "FirstValue";
// var parameter2 = "SecondValue";
return new MyService(parameter1, parameter2);
});
...
// Resolve dependency in the constructor
public class Test
{
private readonly IMyService _myService;
public Test(IMyService myService)
{
_myService = myService;
}
}
你说:
我希望能够在 MAUI 中注册服务,这样我就能够在实例化时传递给它们的构造函数的参数。
啊!因此,也许要做的第一件事是推迟实例化,直到稍后这些参数已知为止,但是当我们构建 DI 时,会传递一个委托,以便在实例化发生时以及如果实例化发生并且调用构造函数时进行调用。例如,下面的代码为
MainPage
提供了根据首次使用服务时的值来初始化服务的机会(但“现在”并不知道,如果像服务器这样的东西可能会出现这种情况)或者必须在运行时、调用服务之前选择根文件夹)。
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// The "ServiceHelper" can be provided by any object you choose.
EventHandler<ConstructorParametersRequiredEventArgs> ServiceHelperMethod =
MainPage.OnServiceConstructing;
builder.Services.AddTransient(
_ => new Lazy<ICounterService>(() =>
new CounterService(onConstructing: MainPage.OnServiceConstructing)));
builder.Services.AddTransient<MainPage>();
return builder.Build();
}
}
默认的 MAUI 项目应用程序非常熟悉,当然有一个
int
计数字段是正确的选择。但我们需要一个服务来进行实验,因此在本例中我们将使用 CounterService
来代替,并使其在第一次单击按钮时服务延迟初始化。在其构造函数中,它将调用在 CreateMauiApp()
方法中预先指定的委托。
现在,
MainPage
(或其他提供者)将能够权衡构建的参数是什么,这甚至可以包括readonly
、仅get
和用于初始化的init
属性服务。
public partial class MainPage : ContentPage
{
private readonly Lazy<ICounterService> _counterService;
public MainPage(Lazy<ICounterService> counterService)
{
InitializeComponent();
_counterService = counterService;
}
internal static void OnServiceConstructing(object? service, ConstructorParametersRequiredEventArgs e)
{
if (service is CounterService counterService)
{
foreach(var key in e.Keys)
{
switch (key)
{
case "_count": // This needs to be "one less" than the first click value we want to see.
e[key] = 4;
break;
}
}
}
}
private void OnCounterClicked(object sender, EventArgs e)
{
int count = _counterService.Value.IncrementCount();
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
SemanticScreenReader.Announce(CounterBtn.Text);
}
}
public interface ILazyService { }
public interface ICounterService : ILazyService
{
int IncrementCount();
int GetCount();
}
public class CounterService : ICounterService
{
private int _count;
// Request initialization parametes in C'TOR
public CounterService(EventHandler<ConstructorParametersRequiredEventArgs> onConstructing)
{
var e = new ConstructorParametersRequiredEventArgs
{
{ nameof(_count), default}
};
onConstructing?.Invoke(this, e);
foreach (var key in e.Keys)
{
switch (key)
{
case "_count":
e.TryGetValue<int>(key, out _count);
break;
}
}
}
public int IncrementCount()
{
_count++;
return _count;
}
public int GetCount()
{
return _count;
}
}
public class ConstructorParametersRequiredEventArgs : EventArgs, IEnumerable<KeyValuePair<string, object?>>
{
private Dictionary<string, object?> _parameters = new ();
public void Add(string key, object? value)
{
_parameters.Add(key, value);
}
public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
{
return _parameters.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public string[] Keys => _parameters.Keys.ToArray();
public object? this[string key]
{
get => _parameters[key];
set => _parameters[key] = value;
}
public bool TryGetValue<T>(string key, out T? value)
{
if (_parameters.TryGetValue(key, out var o) && o is T foundT)
{
value = foundT;
return true;
}
value = default;
return false;
}
}