.net 具有两个依赖服务的循环依赖

aor9mmx1  于 2023-05-02  发布在  .NET
关注(0)|答案(1)|浏览(132)

我是C#和依赖注入的新手。目前我正在做一个新的项目,想做一个技术进步。
在这个项目中,我有三种情况导致循环依赖。
我已经读了很多关于这个问题的文章,并找到了像Lazy<T>IServiceProvider这样的解决方案,但我想学习一个干净的解决方案来解决这个问题,并希望遵循最常见的建议来重构代码。
在这个例子中,我们有四个服务:
AccountService-〉登录、注销等
HttpService-〉执行API操作
LogService-〉执行一些日志记录
LogRepository-〉EF的日志表/ Package 器的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);
    }
}

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);
    }
}

如何解决或适当重新设计这一问题的最佳实践?
我有更多的循环依赖,e。例如,在LogRepository中使用LogService或在HttpService中使用LogService(因为HttpService向服务器发送日志条目)。
非常感谢您的帮助!

jdzmm42g

jdzmm42g1#

虽然你的 * 对象图 * 是循环的(AccountService-〉HttpService-〉AccountService),但你的 * 调用图 * 不是。调用可能如下所示:

AccountService.Login
    -> HttpService.Post
        -> HttpService.AddAuthentication
            -> AccountService.Identity

循环 * 对象图 * 和非循环 * 调用图 * 经常发生在违反Single Responsibly Principle的组件上。类获得的功能越多(方法越多),它们的对象图变得循环的可能性就越大。将类拆分成更小、更集中的部分,不仅解决了循环依赖问题,而且通常还提高了应用程序的可维护性。
我认为您的情况实际上与我在section 6.3 of DIPP&P中讨论的示例非常相似。该部分专门讨论了如何修复循环依赖关系。
长话短说,我认为最好的办法是将AccountService拆分为(至少)两个服务:

  • 一个负责登录和注销的服务
  • 负责获取用户身份的第二个服务。

这两个服务都有自己的接口,与IAccountService相比,这些新接口的宽度更小。这提高了您遵守Interface Segregation Principle的机会。
下面是一个例子,它看起来是这样的:
让我们从新的接口定义开始:

// Contains Login and Logout methods of old IAccountService
public interface IAuthenticationService
{
    Task Login(Credentials Credentials);
    Task Logout();
}

// Contains Identity property of old IAccountService
public interface IIdentityProvider
{
    // For simplicity I added a setter to the interface, because that keeps
    // the example simple, but it is possible to keep Identity read-only if
    // required.
    Identity Identity { get; set; }
}

// This interface is kept unchanged.
public interface IHttpService
{
    Task<T> Get<T>(string uri);
    Task Post(string uri, object value);
    Task<T> Post<T>(string uri, object value);
}

接下来让我们看看实现,从IAuthenticationService实现开始:

// Old AccountService, now depending on IIdentityProvider
public class AuthenticationService : IAuthenticationService
{
    private readonly IHttpService _httpService;
    private readonly ILogService _logService;
    private readonly IIdentityProvider _identityProvider;
    
    public AccountService(
        IHttpService HttpService,
        ILogService LogService,
        IIdentityProvider IdentityProvider)
    {
        _httpService = HttpService;
        _logService = LogService;
        _identityProvider = IdentityProvider;
    }

    public async Task Login(Credentials Credentials)
    {
        _identityProvider.Identity = await _httpService.Post<Identity>(
            "api/rest/v1/user/authenticate", Credentials);
    }
}

这个“新的”AuthenticationService包含了AccountService的部分代码,而旧的AccountService逻辑的其余部分隐藏在新的IIdentityProvider抽象之后,该抽象被注入到AuthenticationService中。这种重构与Facade Service refactoring非常相似(有关Facade Service重构的详细讨论,请参阅DIPP&P的section 6.1)。
IdentityProvider实现新的IIdentityProvider接口,并包含来自AccountService的旧逻辑:

public class IdentityProvider : IIdentityProvider
{
    public Identity Identity { get; set; }
}

最后,HttpService现在依赖于IIdentityProvider而不是IAccountService

// Now depends on IIdentityProvider instead of IAccountService
public class HttpService : IHttpService
{
    private readonly HttpClient _httpClient;
    private readonly IIdentityProvider _identityProvider;
    private readonly ILogService _logService; 

    public HttpService(
        HttpClient HttpClient,
        IIdentityProvider IdentityProvider,
        ILogService ILogService)
    {
        _httpClient = HttpClient;
        _identityProvider = IdentityProvider;
        _logService = LogService;
    }

    private async Task AddAuthentication(HttpRequestMessage Request)
    {
        // Now uses the new IIdentityProvider dependency instead
        // of the old IAccountService, which caused the cycle.
        Request.Headers.Authorization = new AuthenticationHeaderValue(
            "bearer", _identityProvider.Identity.SystemToken);
    }
}

使用这种新设计,对象图不再是循环的,并且可以如下构造:

var identity = new IdentityProvider();
var logger = new LogService();

new AccountService(
    new HttpService(
        new HttpClient(...),
        identity,
        logger),
    logger,
    identity);

相关问题