我有一个需要注入依赖的对象
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上注入依赖关系并设置初始状态?或者,在这种情况下,与框架作斗争是否应该重构代码?
答案 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
。
我希望这会有所帮助。