【问题标题】:Circular reference between the services using the Anemic domain model使用贫血域模型的服务之间的循环引用
【发布时间】:2016-10-17 19:21:57
【问题描述】:

我正在从事一个业务复杂的项目。 考虑两个类:AccountService 和 SchoolService

我正在使用Unity和Web API的依赖解析器在构造函数中实现依赖注入。

学校服务在某些方面使用账户服务,账户服务也使用学校服务。所有这些都是项目业务所必需的。这将导致循环依赖,并且无法将方法从一个类移动到另一个类。

能否请您提供有关如何解决此问题的任何想法?

这是一个例子:

public class SchoolBLC : ISchoolBLC
{
    public School GetSchool(int schoolId)
    {
        ...
    }

    public bool RenewRegistration(int accountId)
    {
        bool result = true;

        IAccountBLC accountBLC = new AccountBLC();
        // check some properties related to the account to decide if the account can be renewed
        // ex : the account should not be 5 years old
        // check the account created date and do renewal

        return result;
    }
}

public class AccountBLC : IAccountBLC
{
    public void ResetAccount(int accountId)
    {
        ISchoolBLC schoolBLC = new SchoolBLC();
        School accountSchool = schoolBLC

        // get the school related to the account to send a notification 
        // and tell the school that the user has reset his account
        // reset account and call the school notification service
    }

    public Account GetAccount(int accountId)
    {
        ...
    }
}

这两个类相互引用,这是项目中 70% 的 BLC 的情况。

【问题讨论】:

  • 你能举一个你的项目中这种循环依赖的例子吗?
  • 听起来像个糟糕的设计...我会将常见的东西分解为第 3 项服务。这将解决循环依赖。 DI 引擎通常会在循环引用上抛出异常。
  • @SledgeHammer 更重要的是。即使没有 DI,您将如何解决问题? DI 并不神奇,如果没有它你做不到,那么你就做不到。
  • 我想补充一下 SledgeHammer 的答案。您的问题是您有两个紧密耦合的类。如果它们有 70% 的耦合度,这是否意味着它们是相同的代码单元?考虑OOP的核心原理,封装;您应该尽可能少地公开功能。当您有两个类的方法仅由彼此调用时,您没有进行封装。这意味着,您的代码不是 OOP。
  • @Aron。不,我不是。我建议如果接口 A & B 有很多通用代码/功能,它应该在基类中或分解为接口 C。然后 A & B 使用接口 C 而不是 B & A 并且你打破了循环引用。

标签: c# dependency-injection circular-dependency anemic-domain-model


【解决方案1】:

如果您绝对必须这样做,您可以拥有一个接口来执行您的 IoC 逻辑并将其解析为包含 Unity 分辨率的实现,例如

public interface ITypeResolver
{
    T Resolve<T>();
}

然后,您可以将该接口传递给构造函数中的两个服务,并使用它在构造函数外部使用它之前延迟解析另一个服务。

这样当两个服务都被初始化时,它们不会直接依赖于另一个服务,只依赖于ITypeResolver

【讨论】:

    【解决方案2】:

    我会按照@KMoussa 的建议做,但会做一些修改:

    该项目使用贫血模型,所以我将使用上下文模式来延迟加载并创建任何服务,并将上下文作为参数传递给服务构造函数。

    public class SDPContext : ISDPContext
    {
        private ITypeResolver _typeResolver;
    
        public Account CurrentUser { get; set; }
    
        public IAccountService AccountService
        {
            get
            {
                // lazy load the account service
            }
        }
    
        public ISchoolService SchoolService
        {
            get
            {
                // lazy load the schoolservice
            }
        }
    
        public SDPContext(ITypeResolver typeResolver)
        {
            this._typeResolver = typeResolver;
        }
    }
    
    public class ServiceBase
    {
        public ISDPContext CurrentContext { get; set; }
    
        public ServiceBase(ISDPContext context)
        {
            this.CurrentContext = context;
        }
    }
    
    public class AccountService : ServiceBase, IAccountService
    {
        public AccountService(ISDPContext context) : base(context)
        {
    
        }
    
        public bool ResetAccount(int accountId)
        {
            // use base.Context.SchoolService to access the school business
        }
    }
    
    public class SchoolService : ServiceBase, ISchoolService
    {
        public SchoolService(ISDPContext context) : base(context)
        {
            //this._accountService = accountService;
        }
    
        public void RenewRegistration(int accountId)
        {
            // use the base.Context.Account service to access the account service
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-02-04
      • 2010-11-04
      • 1970-01-01
      • 2018-12-14
      • 2014-04-19
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 2010-12-26
      相关资源
      最近更新 更多