最近我读过Mark Seemann关于Service Locator反模式的文章.
作者指出ServiceLocator为反模式的两个主要原因:
API使用问题(我完全可以使用)
当类使用服务定位器时,很难看到它的依赖关系,因为在大多数情况下,类只有一个PARAMETERLESS构造函数.与ServiceLocator相比,DI方法通过构造函数的参数显式地暴露依赖关系,因此在IntelliSense中很容易看到依赖关系.
维护问题(让我感到困惑)
请考虑以下示例
我们有一个使用服务定位器方法的类'MyType':
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们要为类'MyType'添加另一个依赖项
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我的误解开始的地方.作者说:
要判断你是否引入了一个重大改变,要变得更加困难.您需要了解使用Service Locator的整个应用程序,并且编译器不会帮助您.
但是等一下,如果我们使用DI方法,我们将在构造函数中引入与另一个参数的依赖关系(在构造函数注入的情况下).问题仍然存在.如果我们忘记设置ServiceLocator,那么我们可能忘记在IoC容器中添加新的映射,并且DI方法将具有相同的运行时问题.
此外,作者还提到了单元测试的难点.但是,我们不会有DI方法的问题吗?我们不需要更新所有实例化该类的测试吗?我们将更新它们以传递一个新的模拟依赖项,以使我们的测试可编译.我没有看到更新和时间花费带来任何好处.
我不是想捍卫Service Locator方法.但这种误解让我觉得我失去了一些非常重要的东西.有人可以消除我的怀疑吗?
更新(摘要):
我的问题"服务定位器是反模式"的答案实际上取决于具体情况.我绝对不会建议你从工具列表中删除它.当您开始处理遗留代码时,它可能会变得非常方便.如果你很幸运能够处于项目的最初阶段,那么DI方法可能是更好的选择,因为它比Service Locator有一些优势.
以下是主要的不同之处,这些差异使我不相信我的新项目使用Service Locator:
有关详细信息,请阅读下面给出的优秀答案.
design-patterns dependency-injection anti-patterns service-locator
我有一个非常基本的MVC控制器,只有一个动作:
public class HomeController : Controller
{
public ActionResult Index()
{
OpenConnection().Wait();
return View();
}
private async Task OpenConnection()
{
var synchronizationContext = SynchronizationContext.Current;
Debug.Assert(synchronizationContext != null);
using (
var connection =
new SqlConnection(
@"Data Source=(localdb)\ProjectsV12;Initial Catalog=Database1;Integrated Security=True;"))
{
await connection.OpenAsync(); // this always hangs up
}
}
}
Run Code Online (Sandbox Code Playgroud)
问题是常规操作(不是异步版本)无法执行异步方法.在我的情况下,OpenConnection()方法总是挂起等待connection.OpenAsync()行.
过了一段时间,我发现了两种使代码正常工作的方法.
使控制器的操作异步
public async Task<ActionResult> Index()
{
await OpenConnection();
return View();
}
Run Code Online (Sandbox Code Playgroud)或者允许异步执行而不捕获原始SychronizationContext - 为此:
await connection.OpenAsync();
用...来代替:
await connection.OpenAsync().ConfigureAwait(false);
所以,我的猜测是我最初的问题是在SynchronizationContext周围.但是SynchronizationContext.Current不是null,这让我想知道我的猜测是否正确.
那么,任何人都可以解释一下,为什么MVC控制器中的异步操作不能同步执行异步方法?
我们使用TFS 2010并有三个分支:Dev - > QA - > Production.
在这个特定的例子中,我们决定将方法参数类型和名称从"Guid reportGuid"更改为"int reportId".我们在QA分支中进行了此更改并将其检入.现在我正在尝试将此更改从QA转移到Dev分支.通常它是在相反的方向完成,但在这种特殊情况下,我们做了我们所做的.这是合并工具的屏幕截图.从截图中可以看出,Dev分支中文件的最后一次登记获得了更改集号30282和QA-31002(比30282更新).至于我,很明显,新的改变应该覆盖旧的改变.但TFS合并的做法恰恰相反.它将来自Dev分支(30282)的旧代码应用于来自QA分支(31002)的新代码,结果不会得到更改.
有人可以解释为什么TFS合并就是这样吗?提前致谢.