1

次のサービス クラスがあります。

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

これらの各サービス クラスは IoC コンテナーによってインスタンス化されており、機能上の問題はありませんが、このアプローチには潜在的な設計上の欠陥があるように感じます。より理にかなった代替アプローチがあるかどうか疑問に思っています。

4

4 に答える 4

7

誰かがすでに指摘しているように、問題は DI コンテナーの制限ではなく、設計にあります。

お互いへの参照を含む別UserServiceの と aがある理由がわかります。JobServiceこれは、 と の両方UserServiceJobService、参照として他のサービスを必要とするいくつかのロジックが含まれているためです (ジョブを追加するには、ユーザーを追加する必要があるなど)。ただし、あるサービスから別のサービスを参照するべきではないと思います。むしろ、サービスが共通のロジックに使用するサービスの背後に別の抽象化レイヤーを用意する必要があります。したがって、サービスには再利用できない (すべきではない) ロジックが含まれ、ヘルパーには共有ロジックが含まれます。

例えば:

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

このようにして、循環参照に関する問題は発生せず、さまざまなサービスで再利用する必要があるロジックを含む場所が 1 か所になります。

また、互いに完全に分離されたサービスを持つことを好みます。

于 2012-12-26T16:34:55.043 に答える
2

あなたが抱えている問題は、実際にはDIコンテナの制限とは何の関係もありませんが、それは一般的な問題です。コンテナがなくても、これらのタイプを作成することは不可能です。

var job = new JobService([what goes here???]);
var user = new UserService(job);

したがって、一般的な答えは、プロパティへの依存関係の1つをプロモートすることです。これにより、依存関係のサイクルが中断されます。

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

// Use property injection
job.User = user;

ただし、厳密に必要な以上のプロパティを使用しないようにしてください。これらの依存関係のサイクルは非常にまれであり、タイプを相互に接続すること、またはDI構成が正しいかどうかを検証することを非常に困難にします。コンストラクター注入により、これがはるかに簡単になります。

于 2012-12-26T16:22:33.370 に答える
1

これはAutofacでは機能しません。ドキュメントの循環依存のセクションを参照してください。

コンストラクター/コンストラクターの依存関係循環コンストラクターの依存関係を持つ2つのタイプはサポートされていません。この方法で登録されたタイプを解決しようとすると、例外が発生します。

関係タイプ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
  }
}        

または、この例の場合deleteUser、メソッドを移動して作成し、ジョブサービス上のすべてのジョブを削除し、ユーザーを参照する代わりにIDを使用することもできます。これにより、IDを使用して依存関係が解消されます。

もう1つの方法は、ジョブサービスをパラメータとしてに渡すことdeleteUserです。

于 2012-12-26T15:51:41.997 に答える
1

イベントを使用してサービスを分離できます。アクションが実行されたときに別のサービスの依存メソッドを呼び出す代わりに、イベントが発生します。インテグレーターは、イベントを通じてサービスを結び付けることができます。サービスは、他のサービスの存在さえ知りません。

public class JobService
{
    public event Action<User, Job> JobAdded;

    public void AddJob(User user, Job job)
    {
        //TODO: Add job.
        // Fire event
        if (JobAdded != null) JobAdded(user, job);
    }

    internal void DeleteJobs(int userID)
    {
        //TODO: Delete jobs
    }
}

public class UserService
{
    public event Action<User> UserDeleted;

    public void DeleteUser(User u)
    {
        //TODO: Delete User.
        // Fire event
        if (UserDeleted != null) UserDeleted(u);
    }

    public void UpdateUser(User user, Job job)
    {
        //TODO: Update user
    }
}

インテグレータがサービスを結び付ける

public static class Services
{
    public static JobService JobService { get; private set; }
    public static UserService UserService { get; private set; }

    static Services( )
    {
        JobService = new JobService();
        UserService = new UserService();

        JobService.JobAdded += JobService_JobAdded;
        UserService.UserDeleted += UserService_UserDeleted;
    }

    private static void UserService_UserDeleted(User user)
    {
        JobService.DeleteJobs(user.ID);
    }

    private static void JobService_JobAdded(User user, Job job)
    {
        UserService.UpdateUser(user, job);
    }
}

(注: イベントの発生を少し単純化しました。このようにスレッドセーフではありません。ただし、イベントは事前にサブスクライブされてお​​り、後で変更されることはないと想定できます。)

于 2012-12-26T17:03:36.987 に答える