Converting the Composite Web Application Block to Unity - Ummmm...Oooops.

This is the forth post in a series. The other post include

If you want background, go read the earlier posts.

Based upon feedback, I am making the source code available at CWAB and Unity.

In the last installment I wanted to remove the following from the ICompositionContainer:

  • static methods
  • Builder
  • Locator
  • Containers
  • Services

We completed the first four. In this installment, we will remove the Services collection, replacing it with using RegisterInstance and Resolve. After that, we will compare our ICompositionContainer to IUnityContainer, see what else we need to do, and may even pull in Unity.

Adding a few new methods

Before we do this, I want to add a generic overload to Resolve and one to RegisterInstance.  I like the way the generic calls on Unity look, compared to all the typeof() and casting we have done so far. 

I added the following tests, one at a time, and made them pass:

 [TestMethod]
public void CanRegisterInstanceViaGenericWithoutName()
{
    ICompositionContainer root = new TestableRootCompositionContainer();
    Foo f1 = new Foo();
    root.RegisterInstance<Foo>(f1);
    Foo returnedFoo = (Foo)root.Resolve(typeof(Foo));
    Assert.AreEqual(f1, returnedFoo);
}

[TestMethod]
public void CanRegisterInstanceViaGenericWithName()
{
    ICompositionContainer root = new TestableRootCompositionContainer();
    Foo f1 = new Foo();
    root.RegisterInstance<Foo>("asdf", f1);
    Foo returnedFoo = (Foo)root.Resolve(typeof(Foo), "asdf");
    Assert.AreEqual(f1, returnedFoo);
}


[TestMethod]
public void CanResolveViaGenericWithoutName()
{
    ICompositionContainer root = new TestableRootCompositionContainer();
    Foo f1 = new Foo();
    root.RegisterInstance<Foo>(f1);
    Foo returnedFoo = root.Resolve<Foo>();
    Assert.AreEqual(f1, returnedFoo);
}

[TestMethod]
public void CanResolveInstanceViaGenericWithName()
{
    ICompositionContainer root = new TestableRootCompositionContainer();
    Foo f1 = new Foo();
    root.RegisterInstance<Foo>("asdf", f1);
    Foo returnedFoo = root.Resolve<Foo>("asdf");
    Assert.AreEqual(f1, returnedFoo);
}

 

In making them pass, I added the following to the ICompositionContainer interface:

 T Resolve<T>();
T Resolve<T>(string name);
void RegisterInstance<TInterface>(TInterface instance);
void RegisterInstance<TInterface>(string name, TInterface instance);

This will help a bit with the look, feel, and style of the code, now for the real work.

Removing the Services Collection

Why do we want to remove the Services collection?  Since Unity handles this sort of functionality, we can offload it entirely to Unity.  This will cut a bit of code out of our implementation.

Let's do this the simple way, (I like to call it hack and slash development) and just remove the Services collection from the ICompositionContainer interface.  This will result in a number of build errors (twenty or so, in fact).  However, we can fix each of them easily.  Any call to add a service becomes a RegisterInstance call, and any call to get a service becomes a Resolve call.  So, we will hack each problem until we are in a green state again.  The other option, for the less daring, is to search for all uses of Services, change them one at a time, and make sure we are green at each step.  Today, I am feeling brave (read: reckless, or overly caffeinated), so I took the other approach.  If I were pairing with someone, they would probably stop me. :-)

Once CWAB compiles, we still need to get the unit test library to compile.  <click click click> <swear> <click click click>.  The biggest challenge here is that Services.AddNew<T, IT> and RegisterType<IT, T> have the generic parameters in reversed order.  Aaargh.

Everything compiles, but I have a problem: 34 unit tests are failing.  This is not optimal.  Now, why are they failing?  After looking at the first few, it looks like I removed Services from the interface, and forgot to remove it from the CompositionContainer.  The result is that some tests are still using the ServicesCollection.  Oooops.

After removing Services from CompostionContainer, and getting everything to compile again, the test results are.....

Ouch!  40 failing unit tests.  That is not good.

However, after some investigation, I can remove a couple of classes from our solution, as they are no longer necessary, and are just causing problems.

 ServiceDependencyParameterResolver
ProviderDependencyParameterResolver
ProviderDependencyAttribute
ServiceDependencyAttribute

After removing them, there is a bit of code cleanup required, to remove references.  All of the ServiceDependencyAttributes I replaced with ObjectBuilder DependencyAttributes.  The ProviderAttributes, I commented out for the moment.

Wow. Progress. we are down to 37 failing tests. After looking at a few of them, where instances were not the same, I changed the SingletonPolicy in Resolve, and we are down to 24 failing tests.

Oh, gotta take a break.

<TimeWarp>Several Days Fly By</TimeWarp>

I got interrupted from getting this working the other day.  Now, I am starting with broken tests.  I hate that.  I guess I was a bit too reckless.  Well, I have a few options:

  1. I can continue and see this through
  2. I can timebox to a hour, and then roll back, and try again by taking smaller steps
  3. I can give up now and try again from the last known good state.

I am going to opt for number 1.5: timebox to two hours, and depending on where I am, make a decision.

<timewarp duration="1 hour" />

@#$%^%$#@#$!!!

<timewarp duration="a few days" reason="I needed to think about it" />

I have determined that I got a bit overzealous.  For the moment, the Services collection is necessary, and we can remove it once we have Unity in place to provide the necessary functionality.  It will stay as part of the interface, and will continue to provide functionality until we no longer need it.  However, it will eventually go away.  How did I arrive at this conclusion?  Well, I could not get the failing unit tests to pass in a reasonable amount of time.  Moving the functionality requires a lot more code than is really practical for throw-away code.  Oooops.  Everyone makes mistakes. 

As a result, I need to roll back a lot of changes.  Unfortunately, I did not back up everything (or do a check in) before starting down the path of removing the services collection.  Net result: I need to roll back everything, and then re-do the first half of this article. Double @#%$@%!

I'll revert and then rework the code up to the section Removing the Services Collection.  I'll get the code posted (with the other source code) before I start the next installment.

However, I wanted to post this article sooner rather than later, so folks don't think I have dropped the series.  Enjoy.