Registering an Instance

Developer
Mar 6, 2009 at 1:13 AM
Looking at other DIs, one feature I saw that is very easy to implement is registering and instance.  I think that it should look like:

public class MyType : IMyType {
   ...
}

and to register an instance

   ...
   var myObj = new MyType();
   // set properties and call methods on myObj

   container.Register<IMyType>(myObj);
or
   container.Register<IMyType>("NAME", myObj);

I am going to work on this and will send the code shortly :)
Mar 6, 2009 at 1:26 AM
I like the idea of an easy overload to do this, but the only caveat is that it will be increasing the size of the compact framework assembly.

Presumably what you're describing could be achieved like this:

    container.Register<IMyType>(c => myObj);

So the function always returns the same instance.

Perhaps this overload could be wrapped in a "#if" so that it doesn't appear in the compact framework? That's a common theme in .NETCF - if there's a way to do it already then they don't bother including an overload to make it easier.

Don't get me wrong - the amount by which such an overload would increase the size of the assembly would be negligible. I just think maybe it's a chance to set a precedent within Funq of conditionally compiling certain bits to keep the CF binaries small.
Coordinator
Mar 6, 2009 at 1:48 AM
I think it's actually a good idea, and the implementation would indeed be the suggested one with a simple lambda.
It would also change the default ownership, as passing an instance also means that it's typically Owner.External by default.
Developer
Mar 6, 2009 at 4:12 AM
Here is the code I created in Container.cs <style type="text/css"> .cf { font-family: Courier New; font-size: 10pt; color: black; background: white; } .cl { margin: 0px; } .cb1 { color: blue; } .cb2 { color: #2b91af; } </style>

        public IRegistration<TService> Register<TService>(TService instance)

        {

            return Register(null, instance);

        }

 

        public IRegistration<TService> Register<TService>(string name, TService instance)

        {

            var entry = RegisterImpl<TService, Func<Container, TService>>(name, null);

            entry.Instance = instance;

            return entry;

        }

and the test for it

<style type="text/css"> .cf { font-family: Courier New; font-size: 10pt; color: black; background: white; } .cl { margin: 0px; } .cb1 { color: #2b91af; } .cb2 { color: blue; } </style>

        [TestMethod]

        public void RegisteredInstanceIsResolved()

        {

            var container = new Container();

 

            var f1 = new Foo();

            container.Register<IFoo>(f1);

 

            var f2 = container.Resolve<IFoo>();

 

            Assert.AreEqual(f1, f2);

 

        }


I haven't considered ownership or ReuseScope issues.  I think ownership can be anything. 
I'll look further.

Developer
Mar 6, 2009 at 4:22 AM
Edited Mar 6, 2009 at 4:32 AM
Ownership and ReuseScope turned out to be easy

        public IRegistration<TService> Register<TService>(TService instance)
        {
            return Register(null, instance);
        }

         public IRegistration<TService> Register<TService>(string name, TService instance)
        {
            var entry = RegisterImpl<TService, Func<Container, TService>>(name, null);
            SaveAndInitializeInstance<TService>(entry, instance);
            return entry;
        }

with a change to CreateAndInitializeInstance to separate the save and initialize steps

        private static TService CreateAndInitializeInstance<TService, TFunc>(ServiceEntry<TService> entry, Func<TFunc, TService> invoker)
        {
            var factory = (TFunc)entry.Factory;
            var instance = invoker(factory);
            return SaveAndInitializeInstance<TService>(entry, instance);
        }

         private static TService SaveAndInitializeInstance<TService>(ServiceEntry<TService> entry, TService instance)
        {
           
// Save instance if Hierarchy or Container Reuse
            if (entry.Reuse != ReuseScope.None)
                entry.Instance = instance; 

            // Track for disposal if necessary
            if (entry.Owner == Owner.Container && instance is IDisposable)
                entry.Container.disposables.Push(new WeakReference(instance)); 

            // Call initializer if necessary
            if (entry.Initializer != null)
                entry.Initializer(entry.Container, instance); 

            return instance;
        }


I haven't created new tests for this change, but all the current tests pass. I've copied the test I create previously, just to have everything in one place.

        [TestMethod]
        public void RegisteredInstanceIsResolved()
        {
            var container = new Container(); 
            var f1 = new Foo();

            container.Register<IFoo>(f1);
            var f2 = container.Resolve<IFoo>();

            Assert.AreEqual(f1, f2);
        }

Coordinator
Mar 6, 2009 at 7:18 AM
any chance that you could send an SVN patch?
Developer
Mar 6, 2009 at 1:46 PM
I've not done that before.  I'll figure it out as I've found a couple of bugs that need to be addressed.
Developer
Mar 6, 2009 at 5:05 PM
I figured out patches and uploaded the required patch for this.

One thing that I missed previously was ensuring that the instance is copied when a Reuse.Container entry is copied to a child.
Coordinator
Mar 8, 2009 at 7:24 AM
Not sure about the copying dennis...
If reuse == hierarchy: no need to copy anything
If reuse == container: I guess we have to copy? what does it mean for an instance/singleton to be reused at the container level? doesn't make much sense to me, looks just the same as a hierarchy reuse, except that if you copy, and dispose a child container, you disposed the same instance that lives in the parent. yuck :S. Not sure what should be the right behavior there. I'm guessing container reuse for provided instances equals hierarchy reuse (only dispose when you dispose the container where you provided the instance in the first place.
If reuse == none: doesn't apply either

I'm leaning towards removing the .ReusedWithin() fluent verb from the API when you're registering a concrete instance, as the other settings don't make sense.
Not sure if that should also be the case with the .OwnedBy(), as provided instances are typically external?
Developer
Mar 8, 2009 at 11:47 PM
I agree with what you are saying.  Ideally, reuse would always be hierachy, and with copying the instance on reuse==container just makes things work if the user choose this type.  Of course, reuse == none is meaningless or wrong as the instance needs to be stored.  Probably should check for this and throw and error, or just make it right.

As to the ownership,  the way funq works allows either to be used.  If owner == container, then the code that creates the instance is relinquishing its responsibility to dispose, and assigning the responsibility to the container.  The way the code in my patch works is only the container that registers the instance can be the owner.  I haven't any checks for an instance being registered in more than one container.  What will happen of owner==container and instance registered in parent and child container and the child container is disposed?

Matthew
Coordinator
Mar 9, 2009 at 12:14 AM

In which case would reuse=container NOT be a mistake? If the object is already in use and available through the parent container... if anything, copying the servicekey and entry + instance would be just a waste for no additional/different behavior...

/kzu from mobile

On Mar 8, 2009 6:47 PM, "mdennis" <notifications@codeplex.com> wrote:

From: mdennis

I agree with what you are saying.  Ideally, reuse would always be hierachy, and with copying the instance on reuse==container just makes things work if the user choose this type.  Of course, reuse == none is meaningless or wrong as the instance needs to be stored.  Probably should check for this and throw and error, or just make it right.

As to the ownership,  the way funq works allows either to be used.  If owner == container, then the code that creates the instance is relinquishing its responsibility to dispose, and assigning the responsibility to the container.  The way the code in my patch works is only the container that registers the instance can be the owner.  I haven't any checks for an instance being registered in more than one container.  What will happen of owner==container and instance registered in parent and child container and the child container is disposed?

Matthew

Read the full discussion online. To add a post to this discussion, reply to this email (funq@disc...

Developer
Mar 9, 2009 at 1:24 AM
Agreed, it may be a waste, but it works.  My preference would be to force reuse=hierarchy to simplify the understandability of the code and functionality and to eliminate the case where the user specifies reuse=NONE and eliminates the issue of parent/child ownership == container issues.

Would you like me to update my patch?
Coordinator
Mar 9, 2009 at 1:28 AM

It might be tricky, but basically what I'm thinking is returning a different IRegistration from that overload, one that doesn't implement IReusedWithin and therefore there's no chance for user mistakes/confusion.

/kzu from mobile

On Mar 8, 2009 8:24 PM, "mdennis" <notifications@codeplex.com> wrote:

From: mdennis

Agreed, it may be a waste, but it works.  My preference would be to force reuse=hierarchy to simplify the understandability of the code and functionality and to eliminate the case where the user specifies reuse=NONE and eliminates the issue of parent/child ownership == container issues.

Would you like me to update my patch?

Read the full discussion online. To add a post to this discussion, reply to this email (funq@disc...