我有一个包含4层的.NET MVC应用程序。我正在尝试使用依赖注入(通过Ninject),但继续实现我真正想到的是服务位置。我目前的问题是:
我有依赖注入设置并处理在我的应用程序层(MVC 5 Web应用程序)中实例化的许多对象。现在,我正在编写审计跟踪插件,并希望它是:
public ActionResult Edit(int id)
{
// ...
AuditTrail.LogVisit("Edit Screen", id);
return View();
}
AuditTrail
是表示审计跟踪表的Entity Framework类(在单独的程序集/层中),LogVisit
是静态方法,因为我不需要现有审计跟踪记录的上下文(这是一个插页)。
AuditTrail.LogVisit
在短时间内创建新的DbContext以插入记录,并且还需要登录的用户ID。登录的用户ID可以通过会话获得,我已经将它暴露为使用InRequestScope
绑定/注入的类的强类型成员 - 希望这可以让我在会话中保留该值但是在不知道System.Web
或类似依赖关系的更高层中访问它。
我在Audit Trail
类中获取注入的用户标识属性/类时遇到问题,因为DbContext的创建与AuditTrail.LogVisit
方法隔离。我想避免传入上下文根目录,因为这似乎不是最佳实践并且会产生其他问题。我查看了工厂扩展,但是从示例中你仍然没有 static 工厂 - 你有一个实例类,它提供了一个工厂类实例。
我可以避免使用静态方法,但是a)只是将问题进一步向下推进(如何在业务层内实例化非静态辅助类?)和b)似乎太有限 - 当你需要时不能使用静态是最相关的实施?
我可以让MVC应用程序句柄实例化所有依赖项并将它们传递给业务层方法,但问题依赖注入是不是试图解决?
答案 0 :(得分:1)
依赖注入是关于注入实例的,因此在此上下文中不能使用静态类和/或方法。整个想法是创建松散耦合的代码,我们这样做是编程到抽象,这意味着接口。接口不适用于静态方法。
会话中的值与DI无关。我们的想法是将定义(接口)与其实现(类)分开。可以在与对应的实现类不同的层中定义接口。这就是它如此强大的原因。您可以在相当低级别的层中定义接口,并将实现放在UI层中。这方面的一个示例是用户上下文类,其中接口驻留在业务层中,实现类位于Web层中,因为这是您要在实现中使用的会话。 DI使您可以在业务层中使用此接口的实现,该接口对Web或会话一无所知。
回到你的案子。根据我的问题,我可以看到以下内容。
在较低层(例如业务层)中,我们定义以下内容:
public interface IUserContext {
int UserId { get; set; }
}
public interface IAuditTrail {
void LogVisit(string controller, int id);
}
此外,在业务(或数据)层中,我们定义了审计跟踪的实现。
public class AuditTrail : IAuditTrail {
public AuditTrail(
Func<DbContext> dbContextFactory,
IUserContext userContext
) {
// omitted: null guards
m_DbContextFactory = dbContextFactory;
m_UserContext = userContext;
}
private readonly DbContextFactory m_DbContextFactory;
private readonly IUserContext m_UserContext;
public void LogVisit(string controller, int id) {
using (var ctx = m_DbContextFactory()) {
var userId = m_UserContext.UserId;
// TODO: Log...
ctx.SaveChanges();
}
}
}
在Web应用程序中,我们定义了用户上下文的实现。
public AspNetMvcUserContext : IUserContext {
private const UserIdSessionKey = "UserId";
public int UserId {
get { return (int)Session[UserIdSessionKey]; }
set { Session[UserIdSessionKey] = value; }
}
}
最后,我们在您的Web应用程序的控制器中使用上述内容。
public class SomeController {
public SomeController(
IAuditTrail auditTrail
) {
// omitted: null guards
m_AuditTrail = auditTrail;
}
private readonly IAuditTrail m_AuditTrail;
public ActionResult Edit(int id) {
m_AuditTrail.LogVisit("Edit Screen", id);
return View();
}
}
当然,您需要在Ninject配置中注册上面的所有组件。
它很干净,可以测试,非常坚固。