2010-06-30 39 views
2

我正在为自己的学习/成长写一个IoC容器。通常我会写一些像下面这样:制定自定义IoC - 如何实施具有范围的DI?

using(DisposableObject dispObj = new DisposableObject()) 
{ 
    UserRepository users = new UserRepository(dispObj); 

    // Do stuff with user. 
} 

将转向:

using(IDisposableObject dispObj = Container.Resolve<IDisposableObject>()) 
{ 
    IUserRepository users = Container.Resolve<IUserRepository>(); 

    // Do stuff with user. 
} 

我怎么能抽象的DisposableObject使其在using范围内使用IOC时的唯一实例化的使用?我试图弄清楚Autofac如何做,但我不完全确定。

编辑:当一个对象被与using范围实例化,所有呼叫解决类型(在这种情况下IDisposableObject)应返回范围的变量,而不是一个新的实例。同样重要的是,在using声明和另一个Resolve<IDisposableObject>被调用后,它会返回一个新的实例。

回答

2

以这种方式使用可丢弃对象时要记住的事情是容器中的引用也会被丢弃,所以最好在通过Resolve返回实例时它需要每次都返回一个新实例。

你需要做的,就是注册时的类型与你的容器,允许它指定的行为,无论是共享(单身),或者非共享,如:

Container.RegisterType<IDisposableObject>(CreationPolicy.NonShared); 

using (var disposable = Container.Resolve<IDisposableObject>()) { 

} 

以上将工作的非共享实例,因为每次都创建一个新实例,所以我们可以安全地处理它。如果您使用CreationPolicy = Shared来尝试上述操作,则Singleton将被丢弃,因此以后的访问可能会导致ObjectDisposedException。

通过,你可以通过传递的creationPolicy =共享创建辛格尔顿情况下,建立这种行为,例如:

Container.RegisterType<IUserRepository>(CreationPolicy.Shared); 

using (var disposable = Container.Resolve<IDisposableObject>()) { 
    var userRepository = Container.Resolve<IUserRepository>(); 
    // only one instance of user repository is created and persisted by the container. 
} 

希望帮助?

如果您以前使用过MEF,这个术语可能很熟悉。

编辑:所以,我的理解是,你想要做的事,如:

using (var repository = Container.Resolve<IUserRepository>()) 
{ 
    var other = Container.Resolve<IUserRepository>(); 
    // should resolve to the same instance. 
} 

你需要找到在容器监测一次性对象的某种方式。也许引进一个额外的创建策略,SharedScope,e.g:

Container.Register<IUserRepository, UserRepository>(CreationPolicy.SharedScope); 

现在,当您使用容器解决型,你需要弄清楚的项目把creationPolicy。如果项目是SharedScope,并且尚未创建,请创建它的一个实例并返回。

如果您解析实例并且它已经创建,则返回现有实例。

当在该项目上调用Dispose时,需要一些回调容器的方法来移除实例。

编辑两个

那么,有没有搞清楚了这一点的一个简单的方法。我能想到的唯一方法是你引入另一个接口:

public interface IMonitoredDisposable : IDisposable 
{ 
    bool IsDisposed { get; set; } 
} 

当一个对象被处置时,确保它设置了IsDisposed属性。那么您可以从您的容器中监控该财产吗?

+0

那种答案我在找。我明白我需要通过某种“范围”。我只想知道如何从我的字典中删除单身人士,如果它已被丢弃?我想到的一种方式是使用(var localContainer = IoC.Create())'为每个“范围”创建一个新的容器,然后调用'localContainer.Resolve ()'在本地执行解析... – TheCloudlessSky 2010-06-30 18:29:23

+0

那么,你不会处理单身人士,否则你将无法再返回他们。如果你的容器有一个Singletons的内部散列表,把它们存储在那里你可以很容易地抓住它们。如果要解析的类型未声明为Singleton,则只需通过Resolve <>将其返回而不存储它。 使用具有Disposable模式问题的容器将不会是一个好的举措,因为您每次都必须设置和创建容器...... – 2010-06-30 19:05:02

+0

因此,如果您不处理Singleton,它们如何具体化到'使用'范围?因为它不被删除,所以散列表中的关键字总是会碰到相同的单身人士(处置或不处理)。我认为你错过了如果在using语句中调用Container.Resolve ()*的问题,它应该引用从'using'范围创建的问题。 – TheCloudlessSky 2010-06-30 21:05:55

2

您可以返回类型为Owned<Foo>的对象而不是Foo

Owned<T>可能是这样的:

public class Owned<T> : IDisposable 
{ 
    private readonly Container container; 
    private readonly T value; 

    public Owned(Container container, T value) 
    { 
     this.container = container; 
     this.value = value; 
    } 

    public T Value { get { return value; } } 

    public void Dispose() 
    { 
     this.container.ReleaseOwned(this); 
    } 

} 

现在客户端拉Owned<Foo>可以通知通过布置它这个对象的容器,即使Foo本身不是一次性的。通过这种方式,容器知道何时清理对象及其依赖关系(这可能也是一次性的,但对客户端不可见),并且可以实现您之后的行为。

Autofac做的事情非常相似。见Nicholas Blumhardt的博客文章The Relationship Zoo,他介绍了这个概念。

+0

昨天晚上我看到你的帖子之前,我最终找到了同样的链接。唯一的问题是,没有Resolve ()然后返回拥有而不是T?那么,不是我的代码总是必须依赖拥有的在'using'语句中,然后拥有。值得到实际的T? – TheCloudlessSky 2010-07-01 11:49:31

+0

调用代码知道它是否需要所有权,所以它可以根据需要调用'Resolve >()'或'Resolve '。或者,如果您不喜欢基于请求类型的语义变化,您可以单独使用'Resolve '和'ResolveOwned '方法。 – 2010-07-02 01:16:41

+0

好酷很有意义,如果我真的需要的东西,不能通知它已被处置。 – TheCloudlessSky 2010-07-02 12:49:34