C# 依赖注入体系结构设计-服务类循环引用

C# 依赖注入体系结构设计-服务类循环引用,c#,dependency-injection,inversion-of-control,circular-dependency,C#,Dependency Injection,Inversion Of Control,Circular Dependency,我有以下服务类别: public class JobService { private UserService us; public JobService (UserService us) { this.us = us; } public void addJob(Job job) { // needs to make a call to user service to update some user info // similar dependenc

我有以下服务类别:

public class JobService {
  private UserService us;

  public JobService (UserService us) {
    this.us = us;
  }

  public void addJob(Job job) {
    // needs to make a call to user service to update some user info
    // similar dependency to the deleteUser method
  }
}

public class UserService {
  private JobService js;
  public UserService(JobService js) {
    this.js = js;
  }

  public void deleteUser(User u) {
    using (TransactionScope scope = new TransactionScope()) {
      List<IJob> jobs = jobService.findAllByUser(u.Id);
      foreach (IJob job in jobs) {
        js.deleteJob(job);
      }
      userDao.delete(user);
      scope.Complete();
    }
  }
}        
公共类就业服务{
私人用户服务;
公共就业服务(UserService us){
this.us=us;
}
公共无效添加作业(作业作业){
//需要致电用户服务以更新某些用户信息
//与deleteUser方法类似的依赖关系
}
}
公共类用户服务{
私人就业服务;
公共用户服务(JobService js){
this.js=js;
}
公共用户(用户u){
使用(TransactionScope范围=新TransactionScope()){
List jobs=jobService.findallbyser(u.Id);
foreach(作业中的IJob作业){
js.deleteJob(job);
}
删除(用户);
scope.Complete();
}
}
}        

这些服务类中的每一个都被IoC容器实例化了,这并不是一个功能问题,但我觉得这种方法有一个潜在的设计缺陷,我想知道是否有一种替代方法更有意义。

这在Autofac中不起作用。请参阅文档的第节

构造函数/构造函数依赖关系两种类型 不支持构造函数依赖项。你会得到一个例外 当您尝试解析以这种方式注册的类型时

您可能会使用(
Func
Lazy
)来打破这种循环

<>你的代码有点太一般,无法找到一个合适的解决方案,但是不管你使用的IoC容器是什么,你都应该考虑改变依赖的方向。
public class JobService {
  private UserService us;

  public JobService (UserService us) {
    this.us = us;
  }

  public void addJob(Job job) {
    // needs to make a call to user service to update some user info
  }
}

public class UserService {
  private JobService js;
  public UserService(Func<JobService> jsFactory) {
    this.js = jsFactory(this);
  }

  public void deleteUser(User u) {
    // needs to call the job service to delete all the user's jobs
  }
}        
公共类就业服务{
私人用户服务;
公共就业服务(UserService us){
this.us=us;
}
公共无效添加作业(作业作业){
//需要致电用户服务以更新某些用户信息
}
}
公共类用户服务{
私人就业服务;
公共用户服务(Func jsFactory){
this.js=jsFactory(this);
}
公共用户(用户u){
//需要调用作业服务来删除用户的所有作业
}
}        
或者,在您的示例中,您可以移动
deleteUser
并创建一个方法,删除作业服务上的所有作业,而不是使用id引用用户。这通过使用id打破了依赖关系


另一种选择是将作业服务作为参数传递给
deleteUser

这在Autofac中不起作用。请参阅文档的第节

构造函数/构造函数依赖关系两种类型 不支持构造函数依赖项。你会得到一个例外 当您尝试解析以这种方式注册的类型时

您可能会使用(
Func
Lazy
)来打破这种循环

<>你的代码有点太一般,无法找到一个合适的解决方案,但是不管你使用的IoC容器是什么,你都应该考虑改变依赖的方向。
public class JobService {
  private UserService us;

  public JobService (UserService us) {
    this.us = us;
  }

  public void addJob(Job job) {
    // needs to make a call to user service to update some user info
  }
}

public class UserService {
  private JobService js;
  public UserService(Func<JobService> jsFactory) {
    this.js = jsFactory(this);
  }

  public void deleteUser(User u) {
    // needs to call the job service to delete all the user's jobs
  }
}        
公共类就业服务{
私人用户服务;
公共就业服务(UserService us){
this.us=us;
}
公共无效添加作业(作业作业){
//需要致电用户服务以更新某些用户信息
}
}
公共类用户服务{
私人就业服务;
公共用户服务(Func jsFactory){
this.js=jsFactory(this);
}
公共用户(用户u){
//需要调用作业服务来删除用户的所有作业
}
}        
或者,在您的示例中,您可以移动
deleteUser
并创建一个方法,删除作业服务上的所有作业,而不是使用id引用用户。这通过使用id打破了依赖关系


另一种选择是将作业服务作为参数传递给
deleteUser

您遇到的问题实际上与DI容器的限制无关,但这是一个一般性问题。即使没有任何容器,也不可能创建这些类型:

var job = new JobService([what goes here???]);
var user = new UserService(job);
因此,一般的答案是将其中一个依赖项提升到属性。这将打破依赖循环:

var job = new JobService();
var user = new UserService(job);

// Use property injection
job.User = user;

但是,请防止使用超出严格要求的属性。这些依赖循环应该非常罕见,这使得将类型连接在一起或验证DI配置的正确性变得更加困难。构造函数注入使这变得更容易。

您遇到的问题实际上与DI容器的限制无关,但这是一个普遍的问题。即使没有任何容器,也不可能创建这些类型:

var job = new JobService([what goes here???]);
var user = new UserService(job);
因此,一般的答案是将其中一个依赖项提升到属性。这将打破依赖循环:

var job = new JobService();
var user = new UserService(job);

// Use property injection
job.User = user;

但是,请防止使用超出严格要求的属性。这些依赖循环应该非常罕见,这使得将类型连接在一起或验证DI配置的正确性变得更加困难。构造函数注入使这变得更加容易。

正如有人已经指出的,问题不在于DI容器的限制,而在于您的设计

我明白了为什么您有一个单独的
UserService
和一个
JobService
,其中包含彼此的引用。这是因为
UserService
JobService
都包含一些需要其他服务作为参考的逻辑(添加作业需要添加用户等)。但是,我认为您不应该从一个服务引用另一个服务。相反,您应该在服务后面有另一层抽象,这些服务将用于公共逻辑。因此,服务将包含不能(不应该)重用的逻辑,而助手将包含共享逻辑

例如:

    public class UserHelper{
      //add all your common methods here
    }
    public class JobService {
      private UserHelper us;

      public JobService (UserHelper us) {
        this.us = us;
      }

      public void addJob(Job job) {
 // calls helper class
      }
    }

    public class UserService {

      public UserService(UserHelper js) {
        this.js = js;
      }

      public void deleteUser(User u) {
        // calls helper class
      }
    }   
这样,循环引用就不会有任何问题,您将有一个地方包含需要由不同服务重用的逻辑

而且,