在n层应用程序中用于库依赖的DI方法

时间:2016-11-15 19:59:56

标签: asp.net-mvc ninject n-tier-architecture ninject.web.mvc

我有一个包含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应用程序句柄实例化所有依赖项并将它们传递给业务层方法,但问题依赖注入是不是试图解决?

1 个答案:

答案 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配置中注册上面的所有组件。

它很干净,可以测试,非常坚固。