使用 IoC 处理在单个函数调用中发生变化的依赖关系

发布于 2024-08-28 08:51:21 字数 946 浏览 6 评论 0原文

我们正在尝试找出如何在服务类根据其使用方式具有不同依赖项的情况下设置依赖项注入。在我们的具体案例中,我们有一个 Web 应用程序,其中 95% 的情况下整个请求的连接字符串是相同的(这是一个 Web 应用程序),但有时它可能会发生变化。

例如,我们可能有 2 个具有以下依赖项的类(简化版本 - 服务实际上有 4 个依赖项):

public LoginService (IUserRepository userRep)
{
}

public UserRepository (IContext dbContext)
{
}

在我们的 IoC 容器中,大多数依赖项都是自动连接的,除了我有类似这样的上下文(不是实际代码) ,这是从记忆中...这是 StructureMap): x.ForRequestedType().Use() .WithCtorArg("connectionString").EqualTo(Session["ConnString"]);

对于我们 95% 的 Web 应用程序来说,这非常有效。但是,我们有一些管理类型的功能,必须跨数千个数据库(每个客户端一个)进行操作。基本上,我们想要这样做:

public CreateUserList(IList<string> connStrings)
{
   foreach (connString in connStrings)
   {
       //first create dependency graph using new connection string 
       ????       
       //then call service method on new database
       _loginService.GetReportDataForAllUsers();
   }
}

我的问题是:我们如何为每次循环创建新的依赖关系图,同时维护可以轻松测试的内容?

We are trying to figure out how to setup Dependency Injection for situations where service classes can have different dependencies based on how they are used. In our specific case, we have a web app where 95% of the time the connection string is the same for the entire Request (this is a web application), but sometimes it can change.

For example, we might have 2 classes with the following dependencies (simplified version - service actually has 4 dependencies):

public LoginService (IUserRepository userRep)
{
}

public UserRepository (IContext dbContext)
{
}

In our IoC container, most of our dependencies are auto-wired except the Context for which I have something like this (not actual code, it's from memory ... this is StructureMap):
x.ForRequestedType().Use()
.WithCtorArg("connectionString").EqualTo(Session["ConnString"]);

For 95% of our web application, this works perfectly. However, we have some admin-type functions that must operate across thousands of databases (one per client). Basically, we'd want to do this:

public CreateUserList(IList<string> connStrings)
{
   foreach (connString in connStrings)
   {
       //first create dependency graph using new connection string 
       ????       
       //then call service method on new database
       _loginService.GetReportDataForAllUsers();
   }
}

My question is: How do we create that new dependency graph for each time through the loop, while maintaining something that can easily be tested?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

摇划花蜜的午后 2024-09-04 08:51:21

要将对象的创建推迟到运行时,您可以使用工厂:

public interface ILoginServiceFactory
{
    ILoginService CreateLoginService(string connectionString);
}

用法:

public void CreateUserList(IList<string> connStrings)
{
    foreach(connString in connStrings)
    {
        var loginService = _loginServiceFactory.CreateLoginService(connString);

        loginService.GetReportDataForAllUsers();
    }
}

To defer the creation of an object until runtime, you can use a factory:

public interface ILoginServiceFactory
{
    ILoginService CreateLoginService(string connectionString);
}

Usage:

public void CreateUserList(IList<string> connStrings)
{
    foreach(connString in connStrings)
    {
        var loginService = _loginServiceFactory.CreateLoginService(connString);

        loginService.GetReportDataForAllUsers();
    }
}
绅刃 2024-09-04 08:51:21

在循环内,执行:

container.With("connectionString").EqualTo(connString).GetInstance<ILoginService>()

其中“connectionString”是 ILoginService 具体实现上的字符串构造函数参数的名称。

Within the loop, do:

container.With("connectionString").EqualTo(connString).GetInstance<ILoginService>()

where "connectionString" is the name of a string constructor parameter on the concrete implementation of ILoginService.

煞人兵器 2024-09-04 08:51:21

所以大多数 UserRepository 方法使用从会话获取的单个连接字符串,但是有几个方法需要针对连接字符串列表进行操作?

您可以通过将连接字符串依赖项从 IContext 提升到存储库并添加两个附加依赖项来解决此问题 - 一个上下文工厂和存储库完成其工作可能需要的所有可能连接字符串的列表:

public UserRepository(IContextFactory contextFactory, 
                      string          defaultConnectionString, 
                      List<string>    allConnectionStrings)

然后它的每个方法都可以根据需要构建任意数量的 IContext 实例:

// In UserRepository

public CreateUserList() {
    foreach (string connString in allConnectionStrings) {
        IContext context = contextFactory.CreateInstance(connString);
        // Build the rest of the dependency graph, etc. 
        _loginService.GetReportDataForAllUsers();
    }
}

public LoginUser() {
    IContext context = contextFactory.CreateInstance(defaultConnectionString);
    // Build the rest of the dependency graph, etc.
}

So most UserRepository methods use a single connection string obtained from session, but several methods need to operate against a list of connection strings?

You can solve this problem by promoting the connection string dependency from IContext to the repository and adding two additional dependencies - a context factory and a list of all the possible connections strings the repository might need to do its work:

public UserRepository(IContextFactory contextFactory, 
                      string          defaultConnectionString, 
                      List<string>    allConnectionStrings)

Then each of its methods can build as many IContext instances as they need:

// In UserRepository

public CreateUserList() {
    foreach (string connString in allConnectionStrings) {
        IContext context = contextFactory.CreateInstance(connString);
        // Build the rest of the dependency graph, etc. 
        _loginService.GetReportDataForAllUsers();
    }
}

public LoginUser() {
    IContext context = contextFactory.CreateInstance(defaultConnectionString);
    // Build the rest of the dependency graph, etc.
}
夏夜暖风 2024-09-04 08:51:21

我们最终只是创建一个具体的上下文并注入它,然后更改创建一个更改上下文连接字符串的包装类。看起来效果很好。

We ended up just creating a concrete context and injecting that, then changing creating a wrapper class that changed the context's connection string. Seemed to work fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文