【问题标题】:DI approach for library dependency in n-tier applicationn 层应用程序中库依赖的 DI 方法
【发布时间】:2016-11-15 19:59:56
【问题描述】:

我有一个 4 层的 .NET MVC 应用程序。我正在尝试使用依赖注入(通过 Ninject),但不断意识到我真正考虑的是服务位置。我目前的问题是这样的:

我有依赖注入设置并处理在我的应用程序层(MVC 5 Web 应用程序)中实例化的许多对象。现在,我正在编写审计跟踪插入代码,并希望它是:

public ActionResult Edit(int id)
{
    // ...
    AuditTrail.LogVisit("Edit Screen", id);

    return View();
}

AuditTrail 是表示审计跟踪表的实体框架类(在单独的程序集/层中),LogVisit 是静态方法,因为我不需要现有审计跟踪记录的上下文(这是插入)。

AuditTrail.LogVisit 新建一个 DbContext 短时间插入记录,还需要登录的用户 id。登录的用户 ID 可通过会话获得,我已将其公开为使用 InRequestScope 绑定/注入的类的强类型成员 - 希望这将允许我在会话中保留该值但可以访问它在不知道System.Web 或类似依赖项的更高层中。

我无法在 Audit Trail 类中获取注入的用户 ID 属性/类,因为 DbContext 的创建与 AuditTrail.LogVisit 方法是隔离的。我想避免传入上下文根,因为这似乎不是最佳实践并会产生其他问题。我查看了工厂扩展,但从示例中您仍然没有 static 工厂 - 您有一个提供工厂类实例的实例类。

我可以避免使用静态方法,但是 a) 只会将问题进一步推低(如何在业务层内实例化非静态帮助程序类?)和 b) 似乎限制太多 - 当你需要的时候不能使用静态方法最相关的实现是否位于?

我可以让 MVC 应用程序处理实例化所有依赖项并将它们传递给业务层方法,但这不是依赖注入试图解决的问题吗?

【问题讨论】:

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


    【解决方案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; }
        }
    }
    

    最后,我们在您的网络应用程序的控制器中使用上述内容。

    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 配置中注册上述所有组件。

    它很干净,可测试,而且非常可靠。

    【讨论】:

    • 这行得通(我昨天开始编写类似的代码),但我认为让我感到困惑的是,使用 DI 我的 EF 业务对象(其中一个实例表示数据存储中的数据)需要与这些类型的逻辑规则业务对象分开(其中一个实例表示执行一系列业务规则和逻辑的引擎)。在所有业务规则引擎都包含在一个实例化的注入对象中的前提下,DI 更适合并且工作得更好。
    猜你喜欢
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2017-06-24
    • 2010-10-23
    • 2017-01-04
    • 2017-04-14
    相关资源
    最近更新 更多