创建可变接口实例 - 依赖注入与服务定位器
Creating volatile interfaces instances - dependency injection vs service locator
问题是我有 3 层项目:DataAccess
dll 和 Presentation
dll 依赖于 Logic
dll。在逻辑上,我定义了接口 od IRepository, IMyIdentityUser
等。在 DataAccess 中,我使用 Microsoft Identity 框架向继承 IdentityUser<Guid>
和 IMyIdentityUser
接口的 MyIdentityUser 注册新用户。我也在使用 IoC 容器。
假设我在演示 (MVC) 层方法中调用了“Register
”,参数为“RegisterViewModel viewModel
”,该方法将注册逻辑委托给 Logic
dll 中的某些 class。
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = MyCore.Resolve<IMyIdentityUser>(); //this is service locator
// antipattern and I want to get
// rid of this
user.UserName = model.Email;
user.Email = model.Email;
var userManager = _userManager;
var result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var signInManager = _signInManager;
await signInManager.SignInAsync(user, false, false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
return View(model);
}
如您所见,我正在使用服务定位器获取 MyIdentityUser 的新实例。我不想将其创建为“new MyIdentityUser()
”,因为这迫使我使用与定义了 MyIdentityUser 的 DataAccess dll 的紧密耦合。
另外,我不想在每次创建 MVC 控制器时都在构造函数参数 IMyIdentityUser
中强制 IoC 容器创建新的用户实例。
我想我可以使用某种抽象工厂,比如
interface IMyIdentityUserFactory
{
IMyIdentityUser CreateNewUser(string name, string email); //any other better arguments?
// not like registerViewModel because
// this view model should be defined in presentation logic
}
并将其作为参数传递给控制器构造函数(可能在 facade 参数中与另一个逻辑连接的参数)但我不确定这一点,因为它通常与单独传递相同 IMyIdentityUser
。
有更好的方法吗?
Also, i dont want to have in constructor parameter IMyIdentityUser and
forcing IoC container to create new user instance each time i'm
creating MVC controller.
你的前提从头开始就错了。
- MVC 控制器不是有状态的。每个请求实例化一个 MVC 控制器 (好吧,如果你读到这里的第 2 点,你可能会争辩说如果你实现
IControllerActivator
就可以改变这个默认行为!)
- IoC 容器不一定创建给定注入依赖项的新实例:它取决于组件生命周期。例如,Castle Windsor 将为您提供 transient、singleton、per-request、per-thread、pooled 和其他生命周期。 Transient 将是肯定会创建给定注入依赖项实例的唯一选择。
因此,基于你错误的假设,我肯定会在你需要依赖的地方使用构造函数注入。或者属性注入,但是在实现依赖注入的时候不是首选,因为通常属性注入是可选的。
另一方面,构造注入应该是可行的方法,因为就可测试性而言,每段代码都将独立于其他代码(耦合度较低):您可以自动或手动实例化给定的 class并自动或手动提供其依赖项。
问题是我有 3 层项目:DataAccess
dll 和 Presentation
dll 依赖于 Logic
dll。在逻辑上,我定义了接口 od IRepository, IMyIdentityUser
等。在 DataAccess 中,我使用 Microsoft Identity 框架向继承 IdentityUser<Guid>
和 IMyIdentityUser
接口的 MyIdentityUser 注册新用户。我也在使用 IoC 容器。
假设我在演示 (MVC) 层方法中调用了“Register
”,参数为“RegisterViewModel viewModel
”,该方法将注册逻辑委托给 Logic
dll 中的某些 class。
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = MyCore.Resolve<IMyIdentityUser>(); //this is service locator
// antipattern and I want to get
// rid of this
user.UserName = model.Email;
user.Email = model.Email;
var userManager = _userManager;
var result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var signInManager = _signInManager;
await signInManager.SignInAsync(user, false, false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
return View(model);
}
如您所见,我正在使用服务定位器获取 MyIdentityUser 的新实例。我不想将其创建为“new MyIdentityUser()
”,因为这迫使我使用与定义了 MyIdentityUser 的 DataAccess dll 的紧密耦合。
另外,我不想在每次创建 MVC 控制器时都在构造函数参数 IMyIdentityUser
中强制 IoC 容器创建新的用户实例。
我想我可以使用某种抽象工厂,比如
interface IMyIdentityUserFactory
{
IMyIdentityUser CreateNewUser(string name, string email); //any other better arguments?
// not like registerViewModel because
// this view model should be defined in presentation logic
}
并将其作为参数传递给控制器构造函数(可能在 facade 参数中与另一个逻辑连接的参数)但我不确定这一点,因为它通常与单独传递相同 IMyIdentityUser
。
有更好的方法吗?
Also, i dont want to have in constructor parameter IMyIdentityUser and forcing IoC container to create new user instance each time i'm creating MVC controller.
你的前提从头开始就错了。
- MVC 控制器不是有状态的。每个请求实例化一个 MVC 控制器 (好吧,如果你读到这里的第 2 点,你可能会争辩说如果你实现
IControllerActivator
就可以改变这个默认行为!) - IoC 容器不一定创建给定注入依赖项的新实例:它取决于组件生命周期。例如,Castle Windsor 将为您提供 transient、singleton、per-request、per-thread、pooled 和其他生命周期。 Transient 将是肯定会创建给定注入依赖项实例的唯一选择。
因此,基于你错误的假设,我肯定会在你需要依赖的地方使用构造函数注入。或者属性注入,但是在实现依赖注入的时候不是首选,因为通常属性注入是可选的。
另一方面,构造注入应该是可行的方法,因为就可测试性而言,每段代码都将独立于其他代码(耦合度较低):您可以自动或手动实例化给定的 class并自动或手动提供其依赖项。