结构图和创建具有初始状态的对象

时间:2011-01-03 18:35:27

标签: c# dependency-injection structuremap

我有一个需要注入依赖的对象

public class FootballLadder
{
    public FootballLadder(IMatchRepository matchRepository, int round)
    {
        // set initial state
        this.matchRepo = matchRepository;
        this.round = round;
    }

    public IEnumerable<LadderEntry> GetLadderEntries()
    {
        // calculate the ladder based on matches retrieved from the match repository
        // return the calculated ladder
    }

    private IMatchRepository matchRepo;
    private int round;
}

为了论证,我们假设我无法将round参数传递给GetLadderEntries调用本身。

使用StructureMap,如何在IMatchRepository上注入依赖关系并设置初始状态?或者,在这种情况下,与框架作斗争是否应该重构代码?

2 个答案:

答案 0 :(得分:2)

您始终可以使用构造函数参数作为默认值。我使用以下内容作为sqlconnection的默认实例。

this.For<SqlConnection>().Use(c => new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString));

还有其他方法,但我不记得它们。

编辑:这是另一种可以做到的方式。我从这里发现了这个: http://www.theabsentmindedcoder.com/2010/05/structure-map-26-constructor-arguments.html

x.For<MatchRepository>().Use<MatchRepository>();
x.For<IFootballLadder>().Use<FootballLadder>()
    .Ctor<int>("round")
    .Is(3);

如果round的值是从一个方法确定的,你可以用lambda表达式指定它来加载这样的值

.Is(c => c.GetInstance<IRoundProvider>().GetRound())

希望这是有道理的。但是回答你的问题是可能而且非常容易。

答案 1 :(得分:1)

大多数DI框架允许您在Spinon向您展示的构造函数中注入基元。在可能的情况下,我尝试以不需要复杂配置的方式重构我的代码。通常这会使我的应用程序代码最容易理解,最少惊喜(每分钟WTF数量少;-))。您必须仔细权衡这一点,因为有时复杂的配置可能会使您的应用程序代码更简单。

以下是重构的一些可能建议:

1)使用工厂:

当客户必须控制round值时,使用工厂非常有用:

public interface IFootballLadderFactory
{
    FootballLadder CreateNew(int round);
}

这样您就可以注入IFootballLadderFactory并允许客户呼叫:

var ladder = this.footballLadderFactory.CreateNew(3);

2)使用属性:

您可以从构造函数中删除round参数,并将其更改为get / set属性。当客户端必须能够控制round值或使用工厂时,这非常有用:

public class FootballLadder
{
    private IMatchRepository matchRepo;

    public FootballLadder(IMatchRepository matchRepository)
    {
    }

    public int Round { get; set; }
}

例如IFootballLadderFactory的实现可能如下所示:

public class CastleFootballLadderFactory : IFootballLadderFactory
{
    public IWindsorContainer Container;

    public FootballLadder CreateNew(int round)
    {
        var ladder = this.Container.Resolve<FootballLadder>();
        ladder.Round = round;
        return ladder;
    }
}

或者客户可以设置Round属性:

public class Client
{
    public Client(FootballLadder ladder)
    {
       ladder.Round = 3;
    }
}

请注意最后一个例子。客户端通常不必关心依赖的生命周期。在这种情况下,我们正在改变注入依赖的状态。这可以防止我们更改此依赖项的生命周期,因为在这种情况下,ladder实例的状态可以从客户端的脚下更改。除此之外,当FootballLadder从未设置时,InvalidOperationException类应该抛出Round。我认为这样的检查很干净,但确实会让你写一些代码。

3)将IRoundProvider注入FootballLadder构造函数:

正如Spinon所写,您可以实现IRoundProvider,但不是在配置中使用它,而是可以将其用作构造函数参数。

public class FootballLadder
{
    private IMatchRepository matchRepo;
    private int round;

    public FootballLadder(IMatchRepository matchRepository,
        IRoundProvider roundProvider)
    {
       this.round = roundProvider.GetRound();
    }        
}

4)创建特定于DI配置的子类型:

public class DIFootballLadder : FootballLadder
{
    private const int Round = 3;

    public DIFootballLadder(IMatchRepository matchRepository)
        : base(matchRepository, Round)
    {
    }
}

现在您可以按如下方式注册:

x.For<FootballLadder>().Use<DIFootballLadder>();

这方面的缺点是你有这个额外的代码本身是普通的配置代码。除此之外,当FootballLadder的依赖关系发生变化时,您还必须更改DIFootballLadder

我希望这会有所帮助。