C# 具有两个依赖服务的循环依赖关系

C# 具有两个依赖服务的循环依赖关系,c#,.net,rest,dependency-injection,webassembly,C#,.net,Rest,Dependency Injection,Webassembly,我对C#和依赖注入非常陌生。目前我正在做一个新项目,希望在技术上向前迈进一步 在这个项目中,我有三种情况导致循环依赖 我已经读了很多关于这方面的书,并找到了类似于Lazy和IServiceProvider的解决方案,但我想学习一个干净的解决方案来解决这个问题,并想遵循最常见的建议来重构代码 在本例中,我们有四项服务: AccountService->登录、注销等 HttpService->做API工作 LogService->做一些日志记录 LogRepository->EF的日志表/包装器的C

我对C#和依赖注入非常陌生。目前我正在做一个新项目,希望在技术上向前迈进一步

在这个项目中,我有三种情况导致循环依赖

我已经读了很多关于这方面的书,并找到了类似于
Lazy
IServiceProvider
的解决方案,但我想学习一个干净的解决方案来解决这个问题,并想遵循最常见的建议来重构代码

在本例中,我们有四项服务:

AccountService
->登录、注销等

HttpService
->做API工作

LogService
->做一些日志记录

LogRepository
->EF的日志表/包装器的CRUD

AccountService
使用
HttpService
通过API进行身份验证。稍后,我想使用
HttpService
通过API获取更多数据
HttpService
现在需要
AccountService
来获取用于验证请求的令牌。这会导致循环依赖项错误

会计服务

public interface IAccountService
{
    Identity Identity { get; }
    Task Login(Credentials Credentials);
    Task Logout();
}

public class AccountService : IAccountService
{
    public Identity Identity { get; private set; }
    
    private readonly IHttpService _httpService;
    private readonly ILogService _logService;
    
    public AccountService(
        IHttpService HttpService, ILogService LogService)
    {
        _httpService = HttpService;
        _logService = LogService;
    }

    public async Task Login(Credentials Credentials)
    {
        Identity = await _httpService.Post<Identity>(
            "api/rest/v1/user/authenticate", Credentials);
    }
}
公共接口服务
{
标识{get;}
任务登录(凭证);
任务注销();
}
公共类AccountService:IAccountService
{
公共标识{get;private set;}
专用只读IHttpService_httpService;
专用只读ILogService(日志服务);;
公共会计服务(
IHttpService HttpService、ILogService LogService)
{
_httpService=httpService;
_logService=logService;
}
公共异步任务登录(凭据)
{
Identity=wait\u httpService.Post(
“api/rest/v1/user/authenticate”,凭证);
}
}
HttpService

public interface IHttpService
{
    Task<T> Get<T>(string uri);
    Task Post(string uri, object value);
    Task<T> Post<T>(string uri, object value);
}

public class HttpService : IHttpService
{
    private readonly HttpClient _httpClient;
    private readonly IAccountService _accountService;
    private readonly ILogService _logService; 

    public HttpService(
        HttpClient HttpClient,
        IAccountService AccountService,
        ILogService ILogService)
    {
        _httpClient = HttpClient;
        _accountService = AccountService;
        _logService = LogService;
    }

    private async Task AddAuthentication(HttpRequestMessage Request)
    {
        Request.Headers.Authorization = new AuthenticationHeaderValue(
            "bearer", _accountService.Identity.SystemToken);
    }
}
公共接口IHttpService
{
任务获取(字符串uri);
任务Post(字符串uri、对象值);
任务Post(字符串uri、对象值);
}
公共类HttpService:IHttpService
{
私有只读HttpClientu HttpClient;
私有只读IAccountService\u accountService;
专用只读ILogService(日志服务);;
公共HTTPS服务(
HttpClient HttpClient,
IAccountService AccountService,
ILogService(ILogService)
{
_httpClient=httpClient;
_accountService=accountService;
_logService=logService;
}
专用异步任务AddAuthentication(HttpRequestMessage请求)
{
Request.Headers.Authorization=新建AuthenticationHeaderValue(
“持票人”,_accountService.Identity.SystemToken);
}
}
如何解决或正确重新设计此问题的最佳实践

我有更多的循环依赖性,例如在
LogRepository
中使用
LogService
,或者在
HttpService
中使用
LogService
(因为
HttpService
将日志条目发送到服务器)

非常感谢你的帮助

尽管对象图是循环的(
AccountService
->
HttpService
->
AccountService
),但调用图不是循环的。可能的调用如下所示:

AccountService.Login
    -> HttpService.Post
        -> HttpService.AddAuthentication
            -> AccountService.Identity
具有非循环调用图的循环对象图通常发生在违反。类获得的功能(方法)越多,其对象图循环的可能性就越大。将类拆分为更小、更集中的部分,不仅可以解决循环依赖性问题,而且通常还可以改进应用程序的设计

我认为你的情况实际上与我在中讨论的例子非常相似。该部分专门讨论修复循环依赖项

长话短说,我认为您最好的选择是将
AccountService
拆分为(至少)两种服务:

  • 一个服务负责登录和注销
  • 负责获取用户身份的第二个服务
这两种服务都有自己的接口,与
IAccountService
相比,这些新接口的宽度现在更小了。这将提高您遵守规则的机会

下面是一个这样的例子:

让我们从新的接口定义开始:

//包含旧IAccountService的登录和注销方法
公共接口IAAuthenticationService
{
任务登录(凭证);
任务注销();
}
//包含旧IAccountService的标识属性
公共接口IIdentialProvider
{
//为了简单起见,我在接口中添加了一个setter,因为这样可以保持
//示例很简单,但如果
//必需的。
标识{get;set;}
}
//此接口保持不变。
公共接口IHttpService
{
任务获取(字符串uri);
任务Post(字符串uri、对象值);
任务Post(字符串uri、对象值);
}
下面让我们看一下实现,从
IAuthenticationService
实现开始:

//旧的AccountService,现在取决于IIdentialProvider
公共类AuthenticationService:IAAuthenticationService
{
专用只读IHttpService_httpService;
专用只读ILogService(日志服务);;
专用只读IIdentialProvider _identityProvider;
公共会计服务(
IHTTP服务HttpService,
ILogService日志服务,
IIdentityProvider IdentityProvider)
{
_httpService=httpService;
_logService=logService;
_identityProvider=identityProvider;
}
公共异步任务登录(凭据)
{
_identityProvider.Identity=wait_httpService.Post(
“api/rest/v1/user/authenticate”,凭证);
}
}
这个“新的”
AuthenticationService
包含了
AccountService
的部分代码,而旧的
AccountService
逻辑的其余部分隐藏在新的
IIdentityProvider
抽象之后,该抽象正在