【问题标题】:structuremap: Setting all properties of an object using setter injection结构图:使用 setter 注入设置对象的所有属性
【发布时间】:2018-04-27 15:46:50
【问题描述】:

我使用structuremap.net 作为我的容器。我想将一个类的所有依赖项作为依赖项对象而不是每个依赖项传递一个参数。

// Dependencies of Controller
class Dependencies {
    public IUserDatabase UserDatabase { get; set; }
    public IProductDatabase ProductDatabase { get; set; }
    // more dependencies ...
}

class Controller {
    Dependencies _d;

    Controller(Dependencies dependencies){
        this._d = dependencies;
    }

    public ActionResult Action() {
        var user = _this._d.GetUserById(10);
        var product = _this._d.GetProductById(20);
        // ...
    }
}

在通过结构映射构建类Dependencies 的实例时,如何使用setter 注入(IUserDatabaseIProductDatabase 等)自动设置所有依赖项?

var container = new Container();
// How to configure this in a generic way?
var dependencies = container.GetInstance<Dependencies>();

编辑

我的第一个例子似乎没有说明为什么我要为一个完整的对象注入 setter 以及为什么这不是一个糟糕的设计选择。

我们有一个自定义的 Web 框架,它采用了 ASP.NET MVC 的许多原则,但在很多方面也有所不同。与 ASP.NET MVC 一样,我们也有动作和控制器以及控制器内的组动作。典型代码如下所示:

class MyController {
    public IUserDatabase _userDatabase; 
    public IProductDatabase _productDatabase;
    public IRatingDatabase _ratingDatabase;
    public ICommentsDatabase _commentsDatabase; 

    MyController(IUserDatabase userDatabase, IUserDatabase productDatabase, IRatingDatabase ratingDatabase, ICommentsDatabase commentsDatabase){
        _userDatabase = userDatabase;
        _productDatabase = productDatabase;
        _ratingDatabase = ratingDatabase;
        _commentsDatabase = commentsDatabase;
    }

    public ActionResult Action1(){
        var user = _userDatabase.GetById(10);
        var product = _productDatabase.GetById(20);
    }

    public ActionResult Action2() {
        var ratings = _ratingDatabase.GetById(20);
        var comments = _commentsDatabase.GetById(20);
    }
}

这个控制器有两个动作。这些操作都没有使用控制器的所有依赖项。此外,通过构造函数注入将依赖项传递给控制器​​会产生很多噪音。

相反,我想将动作的依赖项直接作为参数传递给该动作。我们有自己的路由可以轻松支持这一点。

class Action1Dependencies {
    public IUserDatabase UserDatabase {get; set;}
    public IProductDatabase ProductDatabase {get; set;}
}

class Action2Dependencies {
    public IRatingDatabase RatingDatabase  {get; set;}
    public ICommentsDatabase CommentsDatabase  {get; set;}
}

class MyController {
    public static ActionResult Action1(Action1Dependencies d){
        var user = d.UserDatabase.GetById(10);
        var product = d.ProductDatabase.GetById(20);
    }   

    public static ActionResult Action2(Action2Dependencies d) {
        var ratings = d.RatingDatabase.GetById(20);
        var comments = d.CommentsDatabase.GetById(20);
    }
}

因此,我想拨打 var dependencies = container.GetInstance&lt;Action2Dependencies&gt;(); 之类的电话,并让 StructureMap 通过 setter 注入设置 Action2Dependencies 的所有属性。

使用 C# 7 并支持tuples,上面的代码也可能变成

class MyController {
    public static ActionResult Action1((IUserDatabase UserDatabase, IProductDatabase ProductDatabase) d){
        var user = d.UserDatabase.GetById(10);
        var product = d.ProductDatabase.GetById(20);
    }   

    public static ActionResult Action2((IRatingDatabase RatingDatabase, ICommentsDatabase CommentsDatabase) d) {
        var ratings = d.RatingDatabase.GetById(20);
        var comments = d.CommentsDatabase.GetById(20);
    }
}

【问题讨论】:

  • 为什么要这样做?这是一个坏主意,因为如果您更改了依赖项,您将有多个位置需要编辑,并且这样做没有任何好处......
  • 修改 Dependencies 类,使其具有一个预期所有内部依赖项的构造函数,然后 StructureMap 将为您处理注入。另外,如果你这样做是因为你有很多依赖项,你可能有兴趣查看github.com/jbogard/MediatR
  • 虽然可以在Aggregate Service 后面抽象一组依赖关系,但我认为您在这里缺少抽象。为了简化Action,更常见的做法是将这些依赖关系及其逻辑移至业务层,并让此业务组件以为控制器量身定制的方式返回数据。这样,Controller 中就没有什么行为了。
  • 请在我的问题中查看我的编辑。

标签: c# dependency-injection structuremap


【解决方案1】:

正如 @haim770 建议的那样,您必须在您的 Dependencies 类中创建一个构造函数,该构造函数需要您的所有依赖项。

但在这种情况下,您只需将问题从控制器转移到您的 Dependencies 类。

// Dependencies of Controller
class Dependencies {
    public IUserDatabase UserDatabase { get; set; }
    public IProductDatabase ProductDatabase { get; set; }
    // more dependencies ...

    public Dependencies(IUserDatabase userDatabase, IProductDatabase productDatabase)
    {
        UserDatabase = userDatabase;
        ProductDatabase = productDatabase;
    }
}

然后,你的容器代码就可以工作了。

var container = new Container();
var dependencies = container.GetInstance<Dependencies>();

【讨论】:

    【解决方案2】:

    这可以通过 Setter 注入策略实现,请参阅 http://structuremap.github.io/setter-injection/

    var container = new Container(x => {
        x.Policies.SetAllProperties(
            policy => policy.Matching(              
                property => property.DeclaringType == typeof(Dependencies)
        );
    });
    

    您还可以以更通用的方式定义您的策略,并为您的依赖对象使用标记接口。

    interface IDependencies {}
    property => typeof(IDependencies).IsAssignableFrom(property.DeclaringType)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多