gpt4 book ai didi

c# - 通过添加新接口(interface)修复循环依赖

转载 作者:行者123 更新时间:2023-12-04 17:13:26 25 4
gpt4 key购买 nike

下面是一本书中的一些代码,它显示了循环依赖的方式:

public interface IAuditTrailAppender {
void Append(Entity changedEntity);
}

public class SqlAuditTrailAppender : IAuditTrailAppender {
private readonly IUserContext userContext;
private readonly CommerceContext context;
private readonly ITimeProvider timeProvider;

public SqlAuditTrailAppender(IUserContext userContext, CommerceContext context, ITimeProvider timeProvider) {
this.userContext = userContext;
this.context = context;
this.timeProvider = timeProvider;
}

public void Append(Entity changedEntity) {
AuditEntry entry = new AuditEntry {
UserId = this.userContext.CurrentUser.Id,
TimeOfChange = this.timeProvider.Now,
EntityId = entity.Id,
EntityType = entity.GetType().Name
};

this.context.AuditEntries.Add(entry);
}
}

public class AspNetUserContextAdapter : IUserContext {
private static HttpContextAccessor Accessor = new HttpContextAccessor();

private readonly IUserRepository repository;

public AspNetUserContextAdapter(IUserRepository repository) {
this.repository = repository;
}

public User CurrentUser {
get {
var user = Accessor.HttpContext.User;
string userName = user.Identity.Name;
return this.repository.GetByName(userName);
}
}
}

public class SqlUserRepository : IUserRepository {
public SqlUserRepository(CommerceContext context, IAuditTrailAppender appender) {
this.appender = appender;
this.context = context;
}

public void Update(User user) {
this.appender.Append(user);
}

public User GetById(Guid id) { ... }

public User GetByName(string name) { ... } // <--- used by CurrentUser property of AspNetUserContextAdapter
}
您可以看到存在循环依赖,如下图所示:
enter image description here
作者说“这种依赖循环通常是由违反单一职责原则(SRP)引起的。为了修复它,作者添加了一个新接口(interface) IUserByNameRetriever:
public interface IUserByNameRetriever {
User GetByName(string name);
}

public class SqlUserByNameRetriever : IUserByNameRetriever {
public SqlUserByNameRetriever(CommerceContext context) {
this.context = context;
}

public User GetByName(string name) { ... }
}

public class SqlUserRepository : IUserRepository {
public SqlUserRepository(CommerceContext context, IAuditTrailAppender appender) {
this.appender = appender;
this.context = context;
}

public void Update(User user) {
this.appender.Append(user);
}

public User GetById(Guid id) { ... }

// public User GetByName(string name) { ... } don't need this method anymore
}

public class AspNetUserContextAdapter : IUserContext {
private static HttpContextAccessor Accessor = new HttpContextAccessor();

private readonly IUserByNameRetriever retriever;

public AspNetUserContextAdapter(IUserByNameRetriever retriever) {
this.retriever = retriever;
}

public User CurrentUser {
get {
var user = Accessor.HttpContext.User;
string userName = user.Identity.Name;
return this.retriever.GetByName(userName);
}
}
}
enter image description here
我能理解 IAuditTrailAppender的介绍如何停止依赖循环,但我觉得这只是一种解决方法。我不明白为什么作者说这个依赖循环是由违反 SRP 引起的。因为如果你看 SqlUserRepository ,其 GetByName方法是在类中拥有它的一种性质,就像 GetById方法(消费者可以通过id搜索用户,当然,消费者通过名字搜索用户是自然的),我不明白为什么有 GetByName SqlUserRepository 中的方法是否违反 SRP?

最佳答案

很难看出,但问题在于 IUserRepository 的定义.
问题是:这个对象是否应该在多个请求中使用,为整个系统提供用户存储库,还是为每个请求创建一个用户存储库给特定请求。两种类型的repository在实践中都用过,但是原来的例子好像是希望两者同时使用,确实是SRP问题。
如果它打算在多个请求中使用,那么它在一个特定用户的上下文中记录其审计跟踪是没有意义的。用户存储库的审计记录器不应该需要 IUserContext .这是不是 作者决定了什么。
作者决定另辟蹊径,构建一个IUserRepository对于每个请求,以便其所有操作都在执行它们的特定用户的上下文中执行——这可能是一个好主意。这意味着审计记录器很好,但你需要 IUserRepository 是没有意义的。首先创建用户上下文。如果没有用户上下文来审核它们,您就无法执行任何危险的用户存储库方法。
因此,您需要一种在没有完整存储库的情况下引导用户上下文的方法。较窄的IUserByNameRetriever类填补了这个角色。它要么无法执行任何需要日志记录的敏感操作,要么通过不需要用户绑定(bind)的其他路径创建足够的日志 IAuditTrailAppender .

关于c# - 通过添加新接口(interface)修复循环依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69069822/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com