首页
学习
活动
专区
圈层
工具
发布

IoC失效
EN

Software Engineering用户
提问于 2015-02-06 21:46:26
回答 3查看 383关注 0票数 4

我有许多服务在启动时被我的国际奥委会容器注册为单例,所有这些服务都有一个构造函数,它接受底层存储提供者使用的连接字符串。

这很好,因为连接字符串是静态的,在会话期间从未更改过,但是,我现在需要实现用户在运行时更改连接字符串的能力(例如,他们可以选择指向本地/云DB)。

最终,这意味着我需要以某种方式使我在IoC容器中的所有服务失效,据我所看到,我选择的IoC容器似乎没有提供这样做的方法,所以我需要想出一个实用的解决方案。

现在有很多方法可以做到这一点,但我正在寻找反馈,建议(如果可能的话)什么将被认为是最优的解决方案。这里是一个简化的服务

代码语言:javascript
复制
public class Service
{
    public Service (string connectionString)
    {
        this.ConnectionString = connectionString;
    }

    // marked as protected as derived services may need access to them
    protected string ConnectionString { get; set; }
    ...
}

以下是它目前的注册方式

代码语言:javascript
复制
public static void StartUp(IAppSettings appSettings)
{
    container.Register<IService, Service> (new Service(appSettings.ConnectionString));
    ...
}

我个人不喜欢的最简单的解决方案是让我的所有服务都引用IAppSettings而不是ConnectionString,并使用只读属性。

代码语言:javascript
复制
protected string ConnectionString { get { return appSettings.ConnectionString; } }

但是,考虑到IAppSettings所需的所有服务都是连接字符串,从建模的角度来看,这似乎是错误的。保持这种方法,但稍微改进它将是引入一个新的接口,它只公开从IAppSettings所需的信息。

代码语言:javascript
复制
public interface IDbSettings
{
    string ConnectionString { get; set; }
}

public class AppSettings : IAppSettings, IDbSettings
{
    ...
}
...
container.Register<IService, Service> (new Service(appSettings as IDbSettings));

我稍微说一句,是因为最终我仍然有同样的问题(服务仍然引用一个简单属性的大对象)。

我想要的方法类型(我觉得最好的方法)是扩展当前的IoC容器,引入一个Invalidate方法,这样我就可以简单地将内部实例设置为null,并在解析后重新初始化它。我看到这个看起来有点像

代码语言:javascript
复制
appSettings.ConnectionString = "database=db1";
container.Register<IService, Service>(new Service(appSettings.ConnectionString));
...
var svc = container.Resolve<IService>(); // connects to db1
appSettings.ConnectionString = "database=db2";
...
var svc = container.Resolve<IService>(); // still connects to db1
container.Invalidate<IService>();
var svc = container.Resolve<IService>(); // connects to db2

对我来说,这给了我全方位的好处。

  • 您完全控制希望更改生效的时间。
  • 它与单例模式保持一致
  • 它意味着唯一改变的是IoC容器。

我会对每个人的想法、批评等感兴趣。

EN

回答 3

Software Engineering用户

发布于 2015-04-08 01:33:14

我觉得你的第二种方法是正确的。

我稍微说一句,是因为最终我仍然有同样的问题(服务仍然引用一个简单属性的大对象)。

其他对象所引用的对象的大小不是问题。他们所指的接口的大小就是问题所在。如果接口仅公开单个属性,则接口由具有1000个属性的对象实现并不重要。

我认为你的第三个解决方案是一个非常糟糕的解决方案。它正在创造一个非常漏水的抽取。为什么负责更改连接字符串的类应该关心使用连接字符串的其他类?为什么IOC容器应该关心其对象的状态,而不仅仅是对其生命周期的管理?

IoC容器的主要目的是管理对象图。它为你处理你的物品的创造和破坏。它允许您更容易地编写遵循依赖反转原则的代码。在这种情况下,依赖项是连接字符串。如果您的许多类只依赖于连接字符串,那么它们应该依赖于只定义连接字符串的接口(以及与该连接字符串具体相关的任何方法/属性)。哪个类实际实现了该接口并提供了该功能(除非该类存在显着的性能问题),这并不重要。

这是倾斜点的一部分。它允许您重构接口后面的类,而不必担心使用接口的类。在这种情况下,如果您创建了连接字符串接口,并且使用了所有这些接口,那么在不影响任何依赖类的情况下,在幕后将appSettings类重构为AppSettings和ConnectionSettings类是轻而易举的。

在这种情况下,遵循依赖反转原则是最好的方法。创建小接口,并让您的代码依赖它。然后,IoC容器可以完成它设计的任务。

票数 2
EN

Software Engineering用户

发布于 2015-02-06 22:21:34

贵公司的服务建设贵吗?拆掉呢?

如果您的对象是单个对象,并且负责在其生命周期内构建和保存到数据库的连接,那么如何处理到数据库的事务或并发请求?

我认为一个更好的解决方案是将您的数据库连接到一个临时生命IDBService中,并为您的服务提供一个依赖于IDBService的短暂生命。

票数 1
EN

Software Engineering用户

发布于 2015-09-05 09:45:59

我不熟悉TinyIoC,但假设您正在缓存,或者可以在某种容器(如map/dictionary )中缓存已注册的服务,您可以将key设置为来自connectionString的散列,将value设置为具体的Service

您可以有一个Service getService(string hash)方法,它将根据传递的哈希返回服务。

代码语言:javascript
复制
Service connection = IoCContrainer.getService(appSettings.ConnectionString.hashCode());
// use the connection
票数 0
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/272386

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档