views:

97

answers:

2

I'm writing an IoC container for my own learning/growth. Normally I would write something like the following:

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

    // Do stuff with user.
}

Would turn to:

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

    // Do stuff with user.
}

How can I abstract the DisposableObject so that it is the only instanced used in the using scope when using an IoC? I'm trying to figure out how Autofac does it but I'm not totally sure.

EDIT: When an object is instantiated with the using scope, all calls to resolve that type (in this case IDisposableObject) should return the scoped variable, and not a new instance. It's also important that after the using statement, and another Resolve<IDisposableObject> is called, it returns a new instance.

+2  A: 

The thing to remember when using a disposable object in this fashion, is that the reference in the container would also be disposed, so ideally when you return an instance through Resolve<> it needs to return a new instance each time.

What you would need to do, is when registering types with your container, allow it to specify a behaviour, either Shared (Singleton), or NonShared, e.g.:

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

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

}

The above would work for NonShared instances, as a new instance is created each time, so we can safely dispose of it. If you tried the above with a CreationPolicy = Shared, the Singleton would be disposed of, so future access will likely result in an ObjectDisposedException.

By building this behaviour in, you can create Singleton instances by passing a CreationPolicy = Shared, e.g.:

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.
}

Hope that helps?

This terminology might be familiar if you have previously used MEF.

EDIT: So, my understanding is that you want to do something like:

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

You need to find some way of monitoring a disposable object in the container. Perhaps introduce an additional creation policy, SharedScope, e.g:

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

Now, when you resolve the type using the container, you'd need to figure out the CreationPolicy of the item. If the item is SharedScope, and it hasn't been created, create an instance of it and return.

If you resolve the instance and it has already been created, return the existing instance.

When Dispose is called on that item, you need some way to callback to the container to remove the instance.

EDIT TWO:

Well, there isn't an easy way of figuring that out. The only way I can think is that you introduce another interface:

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

When an object is disposed, make sure it sets the IsDisposed property. Could you then monitor that property from your container?

Matthew Abbott
That sort of answers what I was looking for. I understand that I'll need to pass in some sort of "scope". I'd just like to know how to remove the singleton from my dictionary if it's been disposed? One way that I thought of was creating a new Container for every "scope" `using(var localContainer = IoC.Create())` and then calling `localContainer.Resolve<T>()` to do resolve locally...
TheCloudlessSky
Well, you wouldn't dispose of Singletons, otherwise you wouldn't be able to return them again. If your Container has an internal hashtable of Singletons, store them there where you can easily grab them again. If the type to be resolved is not declared as a Singleton, you simply return it through Resolve<> without storing it.Using a container with the Disposable pattern problem won't be a great move, as you'd have to setup and create your container each time...
Matthew Abbott
So if you don't dispose of Singletons, how can they be specific to the `using` scope? The key in the hashtable would always hit the same singleton (disposed or not) since it's not removed. I think you're missing the problem that if I call Container.Resolve<IDisposableObject>() *inside* the using statement, it should reference the one created from the `using` scope.
TheCloudlessSky
Singletons are designed to be a single instance. If you dispose them they can no longer be used again. It's because of that you wouldn't use them with a using block.
Matthew Abbott
See my edit above in the original post.
TheCloudlessSky
See my edit in my response.
Matthew Abbott
And it's exactly that "monitoring of the disposed" that I'm still wondering about. How could I go about monitoring something until it's disposed...
TheCloudlessSky
Updated again...
Matthew Abbott
+2  A: 

You could return an object of type Owned<Foo> instead of Foo.

Owned<T> might look like this:

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);
   }

}

Now the client pulling Owned<Foo> can notify the container that is done with the object by disposing it, even if Foo itself is not disposable. This way, the container knows when to clean up the object and its dependencies (which might also be disposable, but invisible to the client), and you can implement the behavior you're after.

Autofac does something very similar. See The Relationship Zoo, a blog post by Nicholas Blumhardt where he introduces this concept.

Wim Coenen
I ended up finding that same link late last night before I saw your post. The only problem is that isn't Resolve<T>() then returning Owned<T> instead of T? Then, isn't my code always going to have to rely on Owned<T> in a `using` statement and then owned.Value to get the actual T?
TheCloudlessSky
The calling code knows whether it wants ownership or not, so it can call `Resolve<Owned<Foo>>()` or `Resolve<Foo>` as desired. Or if you don't like the change of semantics based on the requested type, you could have separate `Resolve<T>` and `ResolveOwned<T>` methods.
Wim Coenen
Ok cool that makes sense if I really need something that can't notify that it's been disposed.
TheCloudlessSky