Problem When Injecting Primitive Value

Nov 26, 2010 at 8:26 AM

Hello,

I'm using Rory Primrose's technique for injecting primitive values into my object. The problem is, when I do, although it succeeds, my object no longer gets its dependencies injected. If I disable the appSettings injector, everything gets back to normal.

Has anyone experienced this? I did try to contact Rory, but couldn't get any comment to be accepted.

http://www.neovolve.com/post/2010/04/23/AppSetting-parameter-injection-in-Unity-2.aspx

Thanks,

RP

Nov 27, 2010 at 7:30 PM

Am I the only who is using primitive value injection?!

Has no-one had problems with this?

RP

Nov 27, 2010 at 7:39 PM

Its seems it's not possible:

http://stackoverflow.com/questions/1370330/unity-injectionproperty-results-in-null-properties

Too bad...

Chris: is there any important reason why this isn't possible?

RP

Nov 28, 2010 at 1:26 AM

Could you be a bit more succinct - what "isn't possible?" This is the first I've heard of Rory Primrose or any techniques. The article you pointed to seemed to talk about injecting values from appsettings using the config file. Could you give a more complete example?

As far as the stackoverflow article is concerned, the behavior (API overrides attributes) is quite deliberate. It's built this way so that there's a way to turn attributes off if you don't want to use them (original author put them in but they don't match what you need for your system, for example). You can simply re-specify the properties in the API to get them injected.

Anyway, I'm not sure "what's not possible", could you give me a more direct statement of the problem?

Thanks,

-Chris

 

Nov 28, 2010 at 9:01 AM

Hi, Chris!
What I mean is:
1 - I have a ParameterInjectionElementExtension-derived extension which creates a ParameterValueElement-derived, similar to Rory's example2 - I have an object registered with Unity3 - In my App.config file, I register my extension and also a property injection for a primitive property declared in my object which uses my ParameterInjectionElementExtension-derived extension4 - I have a dependency property marked with [Dependency] for another object also declared in Unity5 - When I resolve my object, the primitive property gets the expected value, but the dependency property does not6 - If I comment out the property injection for the primitive property and I resolve my object, the dependency property is injected
I am based on the example on Rory's page. I don't do any registration, initialization apart from UnityContainer unity = new UnityContainer(); unity.LoadConfiguration();Any ideas? Is this by design?
Thanks!
RP

Nov 29, 2010 at 3:18 PM

Here's a working example; you will see that if you comment out the extension, the Another property is correctly injected, but otherwise it does not.

App.config:

 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
  </configSections>
  <appSettings>
    <add key="MyTestSetting" value="234234" />
  </appSettings>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <sectionExtension type="AppSettingParameterValueExtension, ConsoleApplication1" />
    <container>
      <register type="IDoSomething, ConsoleApplication1" mapTo="DoSomething, ConsoleApplication1">
        <property name="MyTestSetting">
          <appSetting appSettingKey="MyTestSetting" />
        </property>
      </register>
      <register type="IDoAnotherThing, ConsoleApplication1" mapTo="DoAnotherThing, ConsoleApplication1"/>
    </container>
  </unity>
</configuration>

AppSettingParameterValueExtension.cs:

 

using Microsoft.Practices.Unity.Configuration;

public class AppSettingParameterValueExtension : SectionExtension
{
	public override void AddExtensions(SectionExtensionContext context)
	{
		context.AddElement<AppSettingsParameterValueElement>(AppSettingsParameterValueElement.ElementName);
	}
}

AppSettingsParameterValueElement.cs: 
using System;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

	public class AppSettingsParameterValueElement : ParameterValueElement
	{
		public const String ElementName = "appSetting";

		public AppSettingsParameterValueElement()
		{
		}

		public Object CreateValue(Type parameterType)
		{
			String configurationValue = ConfigurationManager.AppSettings[AppSettingKey];
			Object injectionValue;

			if (parameterType == typeof(String))
			{
				injectionValue = configurationValue;
			}
			else
			{
				TypeConverter converter = GetTypeConverter(parameterType);

				try
				{
					injectionValue = converter.ConvertFromInvariantString(configurationValue);
				}
				catch (NotSupportedException ex)
				{
					const String MessageFormat = "The AppSetting with key '{0}' and value '{1}' cannot be converted to type '{2}'";
					String settingValue = configurationValue ?? "(null)";

					String failureMessage = String.Format(
						CultureInfo.InvariantCulture, MessageFormat, AppSettingKey, settingValue, parameterType.FullName);

					throw new ConfigurationErrorsException(failureMessage, ex);
				}
			}

			return injectionValue;
		}

		public override InjectionParameterValue GetInjectionParameterValue(IUnityContainer container, Type parameterType)
		{
			Object injectionValue = CreateValue(parameterType);

			return new InjectionParameter(parameterType, injectionValue);
		}

		private TypeConverter GetTypeConverter(Type parameterType)
		{
			if (String.IsNullOrEmpty(TypeConverterTypeName) == false)
			{
				Type converterType = Type.GetType(TypeConverterTypeName);

				if (converterType == null)
				{
					const String MessageFormat = "The type '{0}' could not be loaded.";
					String message = String.Format(CultureInfo.InvariantCulture, MessageFormat, TypeConverterTypeName);

					throw new ConfigurationErrorsException(message);
				}

				if (typeof(TypeConverter).IsAssignableFrom(converterType) == false)
				{
					const String MessageFormat = "The type '{0}' does not inherit from '{1}'.";
					String message = String.Format(CultureInfo.InvariantCulture, MessageFormat, TypeConverterTypeName, typeof(TypeConverter).FullName);

					throw new ConfigurationErrorsException(message);
				}

				return (TypeConverter) Activator.CreateInstance(converterType);
			}

			return TypeDescriptor.GetConverter(parameterType);
		}

		[ConfigurationProperty("appSettingKey", IsRequired = true)]
		public String AppSettingKey
		{
			get
			{
				return (String) base [ "appSettingKey" ];
			}

			set
			{
				base [ "appSettingKey" ] = value;
			}
		}

		[ConfigurationProperty("typeConverter", IsRequired = false, DefaultValue = null)]
		public String TypeConverterTypeName
		{
			get
			{
				return (String) base [ "typeConverter" ];
			}

			set
			{
				base [ "typeConverter" ] = value;
			}
		}

	}


IDoSomething.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;

public interface IDoSomething
{
	int MyTestSetting
	{
		get;
		set;
	}

	IDoAnotherThing Another
	{
		get;
		set;
	}

	void Do();
}

IDoAnotherThing.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public interface IDoAnotherThing
{
	void Do();
}
DoSomething.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;

public class DoSomething: IDoSomething
{
	#region IDoSomething Members

	public int MyTestSetting
	{
		get;
		set;
	}

	public void Do()
	{
		throw new NotImplementedException();
	}

	[Dependency]
	public IDoAnotherThing Another
	{
		get;
		set;
	}

	#endregion
}

DoAnotherThing.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class DoAnotherThing: IDoAnotherThing	
{
	#region IDoAnotherThing Members

	public void Do()
	{
		throw new NotImplementedException();
	}

	#endregion
}

Program.cs:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Diagnostics;
using System.Reflection;

class Program
{
	static void Main(string [] args)
	{
		IUnityContainer unity = new UnityContainer();
		unity.LoadConfiguration();

		IDoSomething st = unity.Resolve<IDoSomething>();
	}
}

 

Nov 30, 2010 at 10:25 PM

This behavior is a simple consequence of the fact that the API (or config file) overrides attributes. The easiest way to work around this would be simply configure that other property as well in the config file. It would also be possible (but I don't have time to write it right now) to write a value element extension that says "and look for attributes too". That's just not how it works right now is all.

 

 

Dec 1, 2010 at 10:55 AM

Hello, Chris!

Thanks, now I see what was wrong: I was mixing attributes with XML configuration.

To fix that I wrote a resolution attribute:

 

[Serializable]
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class AppSettingDependencyResolutionAttribute : DependencyResolutionAttribute
{
	public String TypeName
	{
		get;
		set;
	}

	public String TypeConverterName
	{
		get;
		set;
	}

	public AppSettingDependencyResolutionAttribute(String appSettingKey)
	{
		this.AppSettingKey = appSettingKey;
	}

	public String AppSettingKey
	{
		get;
		private set;
	}

	public override IDependencyResolverPolicy CreateResolver(Type typeToResolve)
	{
		return (new AppSettingParameterValueElement() { AppSettingKey = this.AppSettingKey, TypeName = this.TypeName, TypeConverterName = this.TypeConverterName });
	}
}

 

 

Once again, thanks.

RP