ServiceContainer,IoC和一次性物品 [英] ServiceContainer, IoC, and disposable objects

查看:76
本文介绍了ServiceContainer,IoC和一次性物品的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题,我将标记这个主观,因为我认为这会演变为更多的讨论.我希望有一些好主意或发人深省的想法.对于这个冗长的问题,我深表歉意,但您需要了解具体情况.

I have a question, and I'm going to tag this subjective since that's what I think it evolves into, more of a discussion. I'm hoping for some good ideas or some thought-provokers. I apologize for the long-winded question but you need to know the context.

问题基本上是:

  • 您如何处理与IoC容器相关的具体类型?具体来说,由谁负责处置它们,如果需要处置,那么这些知识如何传播到调用代码中?

您要求它们是IDisposposable的吗?如果不是,那么该代码是否适用于未来,还是不能使用一次性物品的规定?如果您在接口和具体类型上强制实施IDisposable-requirements以便于将来使用,则其责任是将对象作为构造函数调用的一部分注入?

Do you require them to be IDisposable? If not, is that code future-proof, or is the rule that you cannot use disposable objects? If you enforce IDisposable-requirements on interfaces and concrete types to be future-proof, whose responsibility is objects injected as part of constructor calls?

编辑:我接受了 @Chris Ballard 的回答,因为它是最接近的我们最终采用的方法之一.

Edit: I accepted the answer by @Chris Ballard since it's the closest one to the approach we ended up with.

基本上,我们总是返回看起来像这样的类型:

Basically, we always return a type that looks like this:

public interface IService<T> : IDisposable
    where T: class
{
    T Instance { get; }
    Boolean Success { get; }
    String FailureMessage { get; } // in case Success=false
}

然后,我们从.Resolve和.TryResolve返回一个实现此接口的对象,以使我们在调用代码中得到的始终是同一类型.

We then return an object implementing this interface back from both .Resolve and .TryResolve, so that what we get in the calling code is always the same type.

现在,实现此接口的对象IService<T>是IDisposable的,应该始终进行处置.程序员不能通过解析服务来决定是否应处置IService<T>对象.

Now, the object implementing this interface, IService<T> is IDisposable, and should always be disposed of. It's not up to the programmer that resolves a service to decide whether the IService<T> object should be disposed or not.

但是,这是至关重要的部分,无论是否应该处理服务实例,这些知识都会被烘焙到实现IService<T>的对象中,因此,如果它是工厂范围的服务(即,每次对Resolve的调用都将结束)并添加一个新的服务实例),则在放置IService<T>对象时将处置该服务实例.

However, and this is the crucial part, whether the service instance should be disposed or not, that knowledge is baked into the object implementing IService<T>, so if it's a factory-scoped service (ie. each call to Resolve ends up with a new service instance), then the service instance will be disposed when the IService<T> object is disposed.

这也使它有可能支持其他特殊作用域,例如池化.现在我们可以说我们需要最少2个服务实例,最多15个,通常是5个,这意味着对.Resolve的每次调用都将从可用对象池中检索一个服务实例,或者构造一个新实例.然后,当处理保存有池服务的IService<T>对象时,该服务实例将释放回其池中.

This also made it possible to support other special scopes, like pooling. We can now say that we want minimum 2 service instances, maximum 15, and typically 5, which means that each call to .Resolve will either retrieve a service instance from a pool of available objects, or construct a new one. And then, when the IService<T> object that holds the pooled service is disposed of, the service instance is released back into its pool.

当然,这使所有代码看起来像这样:

Sure, this made all code look like this:

using (var service = ServiceContainer.Global.Resolve<ISomeService>())
{
    service.Instance.DoSomething();
}

但这是一种干净的方法,并且具有相同的语法,而与所使用的服务类型或具体对象无关,因此我们选择了它作为可接受的解决方案.

but it's a clean approach, and it has the same syntax regardless of the type of service or concrete object in use, so we chose that as an acceptable solution.

以下是关于后代的原始问题

大问题出现在这里:

我们有一个使用的IoC容器,最近我们发现了什么是问题.

We have a IoC container that we use, and recently we discovered what amounts to a problem.

在非IoC代码中,当我们想使用一个文件时,我们使用了这样的类:

In non-IoC code, when we wanted to use, say, a file, we used a class like this:

using (Stream stream = new FileStream(...))
{
    ...
}

毫无疑问,这个类是否是拥有有限资源的东西,因为我们知道必须关闭文件,并且该类本身实现了IDisposable.规则很简单,必须丢弃我们构造一个实现IDisposable的对象的每个类.无话可问.此类的用户不能决定调用Dispose是否是可选的.

There was no question as to whether this class was something that held a limited resource or not, since we knew that files had to be closed, and the class itself implemented IDisposable. The rule is simply that every class we construct an object of, that implements IDisposable, has to be disposed of. No questions asked. It's not up to the user of this class to decide if calling Dispose is optional or not.

好,继续执行IoC容器的第一步.假设我们不希望代码直接与文件对话,而是经过一层间接访问.在此示例中,我们将该类称为BinaryDataProvider.在内部,该类使用的是流,它仍然是可抛弃的对象,因此上述代码将更改为:

Ok, so on to the first step towards the IoC container. Let's assume we don't want the code to talk directly to the file, but instead go through one layer of indirection. Let's call this class a BinaryDataProvider for this example. Internally, the class is using a stream, which is still a disposable object, so the above code would be changed to:

using (BinaryDataProvider provider = new BinaryDataProvider(...))
{
    ...
}

这并没有太大变化.该类仍然实现IDisposable的知识仍然存在,没有任何问题,我们需要调用Dispose.

This doesn't change much. The knowledge that the class implements IDisposable is still here, no questions asked, we need to call Dispose.

但是,假设我们有一些类提供的数据现在不使用任何此类有限的资源.

But, let's assume that we have classes that provide data that right now doesn't use any such limited resources.

上面的代码可以写成:

BinaryDataProvider provider = new BinaryDataProvider();
...

好的,到目前为止很好,但是问题来了.假设我们要使用IoC容器注入此提供程序,而不是依赖于特定的具体类型.

OK, so far so good, but here comes the meat of the question. Let's assume we want to use an IoC container to inject this provider instead of depending on a specific concrete type.

代码将是:

IBinaryDataProvider provider =
    ServiceContainer.Global.Resolve<IBinaryDataProvider>();
...

请注意,我假设有一个可用的独立接口,我们可以通过该接口访问对象.

Note that I assume there is an independent interface available that we can access the object through.

通过上述更改,如果以后我们要使用确实应该丢弃的对象怎么办?没有解决该接口的现有代码都没有编写来处理该对象,那么现在该怎么办?

With the above change, what if we later on want to use an object that really should be disposed of? None of the existing code that resolves that interface is written to dispose of the object, so what now?

我们的观察方式,我们必须选择一种解决方案:

The way we see it, we have to pick one solution:

  • 实施运行时检查,以检查正在注册的具体类型是否实现IDisposable,要求通过其公开的接口也实现IDisposable.这不是一个好的解决方案
  • 在对所使用的接口进行限制之前,它们必须始终继承自IDisposable,以确保面向未来.
  • 强制运行时确保没有具体的类型是IDisposposable的,因为使用IoC容器的代码没有明确处理
  • 只需让程序员检查对象是否实现IDisposable并做正确的事"?
  • 还有其他人吗?

此外,在构造函数中注入对象又如何呢?我们的容器以及我们研究过的其他一些容器都能够将新鲜的对象注入到具体类型的构造函数的参数中.例如,如果我们的BinaryDataProvider需要一个实现ILogging接口的对象,那么如果我们在这些对象上强制执行IDispose-"ability",则它的责任是处置日志记录对象?

Also, what about injecting objects in constructors? Our container, and some of the other containers we've looked into, is capable of injecting a fresh object into a parameter to a constructor of a concrete type. For instance, if our BinaryDataProvider need an object that implements the ILogging interface, if we enforce IDispose-"ability" on these objects, whose responsibility is it to dispose of the logging object?

您怎么看?我想要好与坏的意见.

What do you think? I want opinions, good and bad.

推荐答案

(免责声明:我是根据java的东西来回答这个问题.尽管我编写了C#,但我没有在C#中代理任何东西,但我知道这是可能的.很抱歉Java术语)

(Disclaimer: I'm answering this based on java stuff. Although I program C# I haven't proxied anything in C# but I know it's possible. Sorry about the java terminology)

您可以让IoC框架检查正在构造的对象,以查看其是否支持 IDisposable.如果没有,您可以使用动态代理来包装IoC框架提供给客户端代码的实际对象.该动态代理可以实现IDisposable,以便您始终将IDisposable交付给客户端.只要您使用的接口应该相当简单?

You could let the IoC framework inspect the object being constructed to see if it supports IDisposable. If not, you could use a dynamic proxy to wrap the actual object that the IoC framework provides to the client code. This dynamic proxy could implement IDisposable, so that you'd always deliver a IDisposable to the client. As long as you're working with interfaces that should be fairly simple ?

然后,当对象是一个IDisposable对象时,您将遇到与开发人员 进行通信的问题.我不太确定如何才能很好地完成此操作.

Then you'd just have the problem of communicating to the developer when the object is an IDisposable. I'm not really sure how this'd be done in a nice manner.

这篇关于ServiceContainer,IoC和一次性物品的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆