Constructors

Feb 22, 2008 at 7:48 PM
Chris,

For http://www.codeplex.com/unity/WorkItem/View.aspx?WorkItemId=1307, you said:

"It's trying to create the object with the longest constructor it can find; in this case, the one that takes a string. It then tries to create a string, and can't, so it fails.

To make this work, you have basically two choices:

1) Mark the constructor that doesn't take a string with the InjectionConstructor attribute to force the container to choose it, or
2) Use the RegisterInstance method to add a string to the container so it has a value to feed to that constructor."


This issue was closed, but the comments seem to indicate the provided file was the fix. The inability to create a string seems to be the real issue here, not which constructor was chosen so this issue still exists for classes with a single constructor which takes a string.

Derek



Hope this helps."
Feb 22, 2008 at 8:35 PM
The inability to create a string is going to be an issue regardless. You can't say "new string()" and have it work. There was an actual bug in the choice of constructor which was causing the weirdness with the InjectionConstructor not being the first one. That's fixed.

We had a long conversation at the workshop about this exact issue; it turns out we'll need to make some ObjectBuilder changes to make supplying constructor parameters via config easier. Until then, RegisterInstance is pretty much it.

-Chris
Feb 23, 2008 at 12:08 AM
Edited Feb 23, 2008 at 12:12 AM
There is a Unity thread on Alt.Net that you might want to check out. One of the things that was mentioned was how the different containers would handle the following:

 
container.Register<IDisposable, SqlConnection>("SQL")
   .Register<IDisposable, OracleConnection>("ORA");
 
container.Get<IDisposable>();
 


The issue being discussed was whether the container would throw and exception due to no default mapping of IDisposable, return null, or return SqlConnection instance.

That aside, given the container did resolve IDisposable somehow, apparently the other containers are able to create a new SqlConnection. I guess they do so by preferring no-arg constructors.

Derek
Feb 23, 2008 at 12:18 AM
I'd vote that it should return null. The build key of container.Get<IDisposable>() corresponds to nothing registered.

If you want SqlConnection to be the default, you should register it as so.

Prefering a no-arg constructor can easily be implemented through a custom extension or simply using a static factory extension:

container.Configure<IStaticFactoryConfiguration>().RegisterFactory<SqlConnection>("SQL", () => new SqlConnection());

Of course, you can define extension methods to streamline the process.
Feb 23, 2008 at 1:25 AM
Unity does return null for the example above which I also agree is the preferred default behavior. That aside, it can't currently handle the following:

 
container.Register<IDisposable, SqlConnection>("SQL");
container.Get<IDisposable>("SQL");
 




Feb 23, 2008 at 1:34 AM

ctavares wrote:
... it turns out we'll need to make some ObjectBuilder changes to make supplying constructor parameters via config easier. Until then, RegisterInstance is pretty much it.

-Chris



So, how doyou currently supply parameters?
Feb 23, 2008 at 3:16 AM
Through config but it is still to be implemented. I think we should wait for the next drop or so.

Altough I still think my approach could easily be implemented:

container.RegisterUsingDefaultCtor<IDisposable, SqlConnection("SQL");
container.Get<IDisposable>("SQL")l

using
public static class ContainerExtensions
{
  public static void RegisterUsingDefaultCtor<TFrom, TTo>(this IUnityContainer container, string name)
    where TTo : TFrom, new()
  {
    container.Register<TFrom, TTo>(name);
    container.Configure<IStaticFactoryConfiguration>().RegisterFactory<TTo>(name, () => new TTo());
  }
}

I think this should work....
Feb 24, 2008 at 11:43 PM

francois_tanguay wrote:

container.RegisterUsingDefaultCtor<IDisposable, SqlConnection("SQL");
container.Get<IDisposable>("SQL")l



Yeah, that's actually a pretty cool way to do it given the API doesn't support it directly.
Feb 24, 2008 at 11:58 PM
Chris,

I was curious about what your current thoughts were around how the use of parameters might affect the default choosing of constructors.

 
public class Apple
{
        public Apple() { }
 
        public Apple(string s) { ... }
}
 

Given the above class, you would obviously call the second constructor if you were to specify a string as the parameter to be used when building up the object. However, what will be the behavior when you don't supply parameters? Will you still use the constructor with the most arguments? I ask because once we start supplying parameters then it would seem to make the most sense to call the default constructor if no parameters are specified.

Derek
Feb 25, 2008 at 1:59 AM

francois_tanguay wrote:
I'd vote that it should return null. The build key of container.Get<IDisposable>() corresponds to nothing registered.




BTW Chris, I saw your post on Ayende's blog where you mentioned that Unity's returning a null was actually due to a bug. This wasn't the default behavior of the first Object Builder. What is the rationale for changing this behavior?

Derek
Feb 25, 2008 at 4:30 AM

derekgreer wrote:
Chris,

I was curious about what your current thoughts were around how the use of parameters might affect the default choosing of constructors.

 
public class Apple
{
        public Apple() { }
 
        public Apple(string s) { ... }
}
 

Given the above class, you would obviously call the second constructor if you were to specify a string as the parameter to be used when building up the object. However, what will be the behavior when you don't supply parameters? Will you still use the constructor with the most arguments? I ask because once we start supplying parameters then it would seem to make the most sense to call the default constructor if no parameters are specified.

Derek


The behavior is quite simple. You don't ever supply just parameters, you have to specify which constructor you want as well. If you specify a constructor and parameters, that constructor is called. Otherwise we choose the longest constructor and attempt to resolve the parameters via the container.

Calling the default constructor doesn't work, because what about classes that don't have one? Rather than have two special cases, I prefer the uniformity.
Feb 25, 2008 at 4:35 AM

derekgreer wrote:

francois_tanguay wrote:
I'd vote that it should return null. The build key of container.Get<IDisposable>() corresponds to nothing registered.




BTW Chris, I saw your post on Ayende's blog where you mentioned that Unity's returning a null was actually due to a bug. This wasn't the default behavior of the first Object Builder. What is the rationale for changing this behavior?

Derek


If an object declares a constructor parameter, in general it means that that parameter is a required dependency; it can't work without it. It's just far more bug-prone to return null when in the vast majority of cases, that null was the result of a misconfiguration.

There are several ways to get null if you actually want it, but you shouldn't get it accidentally. It's just too difficult to debug.
Feb 25, 2008 at 2:13 PM
Just so we're clear, I think IUnityContainer.Get<T>(...) should return null when no match is found. That's why I suggested that method being renamed Find.

However, I understand ctor parameter dependencies receiving a null might be difficult to track and debug, that's why I think we should leverage the DependencyAttribute on ctor params as well

public class Foo
{
  public Foo([Dependency(NotPresentBehavior=Throw)] IBar bar) { ... }
}

It becomes an opt-in approach, exactly the same way VAB works with [NotNullValidator]