我在错误使用 HttpClient 时阅读了很多有关套接字耗尽和 DNS 解析问题的内容。推荐的方法是在应用程序的生命周期中通过依赖注入使用 HttpClient 的共享实例。
我对基本依赖注入没有任何问题。
我有一个基类,它使用相关属性初始化客户端,当应用程序加载时,此调用被注册为单例(https://github.com/dotnet/maui/discussions/3201)
public class BaseNippyApiService : IBaseNippyApiService
{
public HttpClient Client { get; private set; }
public BaseNippyApiService()
{
ResiliencePipeline<HttpResponseMessage> retryPipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddRetry(new MobileHttpRetryStrategyOptions())
.Build();
#pragma warning disable EXTEXP0001
ResilienceHandler resilienceHandler = new(retryPipeline)
{
InnerHandler = new SocketsHttpHandler(),
};
Client = new HttpClient()
{
BaseAddress = new Uri("REMOVED"),
Timeout = TimeSpan.FromSeconds(10)
};
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Client.DefaultRequestHeaders.Add("User-Agent", DeviceInfo.Platform.ToString());
if (!SentrySdk.IsEnabled)
{
SentrySdk.Init(options =>
{
options.Dsn = "REMOVED";
});
}
}
}
在毛伊岛程序中注册单例(本文的代码位于一个干净的基本应用程序中用于测试)
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");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
builder.Services.AddSingleton<IBaseNippyApiService, BaseNippyApiService>();
builder.Services.AddSingleton<MainPage>();
return builder.Build();
}
}
在主页上我可以使用构造函数注入来获取客户端,这是一个简单的示例,我在主应用程序中使用虚拟机
IBaseNippyApiService _NippyService;
public MainPage(IBaseNippyApiService bns)
{
InitializeComponent();
_NippyService = bns;
}
问题从这里开始。前两行很好,我可以使用从 MainPage 上的构造函数传递的服务来访问我的其他服务方法。问题出在第3行,在对象_NippyService传递给对象时始终为null
AdCampaignService svc = new AdCampaignService(_NippyService);
List<AdCampaign> campaigns = svc.GetAdCampaignsAsync(subAdminArea);
await campaigns[0].Delete();
我的 AdCampaign 类中有用于创建、更新、删除的方法。
我尝试通过构造函数传入 then 接口,以便我可以使用 httpClient 的共享实例设置 AdCampaign Service 构造函数,始终为 null。
我也尝试在这里获取注册的服务,但调用目标失败(使用 IServiceProvider,默认在 MAUI 应用程序中注册)
public AdCampaign(IBaseNippyApiService bns)
{
if (_AdCampaignService == null)
_AdCampaignService = new AdCampaignService(bns);
}
我尝试了 MAUI 天气示例应用程序中的实用方法来获取服务 https://github.com/davidortinau/WeatherTwentyOne/blob/main/src/WeatherTwentyOne/Services/ServiceExtensions.cs(由 Gerald Versluis (Microsoft) 推荐)
public async Task<CustomWebResponse> Delete()
{
if(_AdCampaignService is null)
{
var service = DataService.Utility.ServiceProvider.GetService<IBaseNippyApiService>();
_AdCampaignService = new AdCampaignService(service);
}
return await _AdCampaignService.DeleteAdCampaignAsync(this.Id);
}
我仍然可以不调用对象上的创建、更新和删除并调用服务方法,而是将对象传递给它们,即 AdCampaign.CreateAsync(adCampaign) 但这对我来说不太合适。
该方法是否错误,我应该只调用对象外部的服务方法,还是应该能够调用调用共享 HttpClient 实例的对象的方法?
首先,感谢您的浏览,我希望这个答案可以帮助那些遇到同样问题的人。互联网上的信息有限,主要是因为您为了找到答案而提出了哪些问题。这需要理解。 。 .
回顾
每次我想从内部实例化一个服务并对象时,注入的值为空。这是构造函数注入的一个误解。
public AdCampaign(IBaseNippyApiService bns)
{
if (_AdCampaignService == null)
_AdCampaignService = new AdCampaignService(bns);
}
毛伊岛计划
您可以在此处注册那些您希望可用于构造函数注入的项目。最初我认为你可以在任何地方使用它们,这是事实,但你需要了解一些关于依赖注入以及如何注入值的知识。
builder.Services.AddSingleton<IBaseNippyApiService, BaseNippyApiService>();
builder.Services.AddSingleton<IAdCampaignService, AdCampaignService>();
IBaseNippyApiService 和 IAdCampaignService 现在都可以通过构造函数注入。
重要
如果实例化对象,则构造函数注入不起作用,因为手动执行了对象的创建。依赖注入不会创建对象,因此不会注入任何值。
AdCampaign campaign = new AdCampaign();
您将需要传递预期的构造函数属性。这导致了我的混乱,我开始从任何地方将属性传递给构造函数。特别是在测试用例中,有效但引起混乱并导致卡住。
BaseNippyApiService baseNippyApiService = new BaseNippyApiService();
AdCampaignService serv = new AdCampaignService(baseNippyApiService);
解决方案
我在 MauiProgram 中注册了 AdCampaignService,使其可用于注入(未创建实例时)
builder.Services.AddSingleton<IAdCampaignService, AdCampaignService>();
在 MauiProgram 中注册的所有服务都可以通过构造函数注入到您的页面或视图模型中。
IBaseNippyApiService _BaseNippyApiService;
IAdCampaignService _AdCampaignService;
public MainPage(IBaseNippyApiService baseNippyApiService, IAdCampaignService adCampaignService)
{
InitializeComponent();
_BaseNippyApiService = baseNippyApiService;
_AdCampaignService = adCampaignService;
}
然后我可以致电服务
List<AdCampaign> campaigns = _AdCampaignService.GetAdCampaignsAsync(subAdminArea);
并且在对象内部(IAdCampaignService 已在 MauiPrograme 中注册,因此值被注入)
private IAdCampaignService _AdCampaignService { get; set; }
public AdCampaign(IAdCampaignService adCampaignService)
{
_AdCampaignService = adCampaignService;
}
public async Task<CustomWebResponse> Delete()
{
return await _AdCampaignService.DeleteAdCampaignAsync(this.Id);
}
然后这允许我调用 .Delete() 因为 _AdCampaignService 不为空,因为构造函数注入有效,因为我没有创建对象,从服务调用返回了一个列表,因此依赖注入执行了构造函数注入,设置 _AdCampaignService
CustomWebResponse response = await campaigns[0].Delete();
希望这有帮助