Problem with generic types...

Mar 30, 2007 at 1:33 AM
I'm trying to implement resolving a generic component, here's my the test thats failing.. how should I tweak my strategies/policies to make this work?

 
[Test]
public void InjectWithGenericConstructorDependency()
{
    // note: this doesn't currently work, need to verify how it should work
    // (checking the forums)
 
    Builder builder = new Builder();
    Locator locator = new Locator();
 
    builder.Policies.Set<ITypeMappingPolicy>(
        new TypeMappingPolicy(typeof(DefaultGenericProducer<>), null),
        typeof(IProducer<>), null);
 
    ConstructorPolicy cp = new ConstructorPolicy();
    cp.AddParameter(new CreationParameter(typeof(IProducer<>)));
 
    builder.Policies.Set<ICreationPolicy>(cp, typeof(GenericConstructorConsumer<>), null);
 
    GenericConstructorConsumer<int> integerConsumer = builder.BuildUp<GenericConstructorConsumer<int>>(locator, null, null);            
 
    Assert.IsNotNull(integerConsumer);
}
 

Any help would be much appreciated...

Chez,

- Alex
Apr 5, 2007 at 4:17 AM
Are you getting an exception? Or is integerConsumer null?
Apr 6, 2007 at 12:55 AM
An exception is being thrown when I attempt to build up the generic type, here is the stack trace:

TestCase 'IoC.Tests.Basic_Container_Tests.ObjectBuilderBasicContainerTests.InjectWithGenericConstructorDependency'
failed: System.ArgumentException : Can not create an instance of type 'IoC.Tests.Basic_Container_Tests.IProducer`1[System.Int32]'.
  ----> System.MemberAccessException : Cannot create an abstract class.
	at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUpNewObject(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.SingletonStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.TypeMappingStrategy.BuildUp(IBuilderContext context, Type t, Object existing, String id)
	at Microsoft.Practices.ObjectBuilder.DependencyResolver.Resolve(Type typeToResolve, Type typeToCreate, String id, NotPresentBehavior notPresent, SearchMode searchMode)
	at Microsoft.Practices.ObjectBuilder.DependencyParameter.GetValue(IBuilderContext context)
	at Microsoft.Practices.ObjectBuilder.ConstructorPolicy.GetParameters(IBuilderContext context, Type type, String id, ConstructorInfo constructor)
	at Microsoft.Practices.ObjectBuilder.CreationStrategy.InitializeObject(IBuilderContext context, Object existing, String id, ICreationPolicy policy)
	at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUpNewObject(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.SingletonStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
	at Microsoft.Practices.ObjectBuilder.TypeMappingStrategy.BuildUp(IBuilderContext context, Type t, Object existing, String id)
	at Microsoft.Practices.ObjectBuilder.BuilderBase`1.DoBuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies)
	at Microsoft.Practices.ObjectBuilder.BuilderBase`1.BuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies)
	at Microsoft.Practices.ObjectBuilder.BuilderBase`1.BuildUp[TTypeToBuild](IReadWriteLocator locator, String idToBuild, Object existing, PolicyList[] transientPolicies)
	C:\dev\home\IoC.Tests\IoC.Tests\Basic Container Tests\ObjectBuilderBasicContainerTests.cs(66,0): at IoC.Tests.Basic_Container_Tests.ObjectBuilderBasicContainerTests.InjectWithGenericConstructorDependency()
	--ArgumentException
	at System.Runtime.Serialization.FormatterServices.nativeGetSafeUninitializedObject(RuntimeType type)
	at System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(Type type)
	at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUpNewObject(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)

Is ObjectBuilder actually capable of handling a scenario where it's smart enough to create a GenericConstructorConstumer<int> and a DefaultGenericeProducer<int> by inferring the type parameter from the type requested in the BuildUp call?
Apr 6, 2007 at 1:12 AM
To further clarify what I'm trying to achieve, basically I have some types like this:

 
    public interface IProducer<T>
    {
        T ProduceSomething();
    }
 
    public class GenericConstructorConsumer<T> : IConsumer
    {
        private readonly IProducer<T> _producer;
 
        public GenericConstructorConsumer(IProducer<T> producer)
        {
            if (producer == null) throw new ArgumentNullException("producer");
            _producer = producer;
        }
 
        public IProducer<T> Producer
        {
            get { return _producer; }
        }
 
        public void Consume()
        {
            Console.WriteLine(_producer.ProduceSomething());
        }
    }

And I would like to be able to have object builder let me do this:

 
GenericConstructorConsumer<int> integerConsumer = builder.BuildUp<GenericConstructorConsumer<int>>(locator, null, null);
GenericConstructorConsumer<string> stringConsumer = builder.BuildUp<GenericConstructorConsumer<string>>(locator, null, null);  
GenericConstructorConsumer<DateTime> dateTimeConsumer = builder.BuildUp<GenericConstructorConsumer<DateTime>>(locator, null, null);
 

Getting the appropriate concrete producer injected for each type of consumer based on the generic type parameter T... without having to individually register each variant in the OB first.

Is this possible?
Apr 7, 2007 at 8:34 AM
The error message from the exception indicates that OB isn't able to find the type mapping in the locator.

Looking at the OB TypeMappingStrategy code (ObjectBuilder\Strategies\TypeMapping\TypeMappingStrategy.cs), it looks like it is searching for the specific type.

You could write your own custom TypeMappingStrategy (GenericTypeMappingStrategy ?) that looked up the policy based on the generic type (without the type parameters), then transferred the type parameters to the mapped type before passing that to BuildUp.

Let me know if you are able to try that out. Otherwise I'll see if I can find time to give it a shot.

Brannon
Apr 7, 2007 at 9:26 AM
Edited Apr 7, 2007 at 9:32 AM
So my idea works. Here's the Strategy and Policy code that you need:

    public class GenericTypeMappingPolicy : ITypeMappingPolicy
    {
        private DependencyResolutionLocatorKey pair;
        
        public GenericTypeMappingPolicy(Type type, string id)
        {
            pair = new DependencyResolutionLocatorKey(type, id);
        }
        
        public DependencyResolutionLocatorKey Map(DependencyResolutionLocatorKey incomingTypeIDPair)
        {
            if (incomingTypeIDPair.Type.IsGenericType)
            {
                Type mappedType = pair.Type.MakeGenericType(incomingTypeIDPair.Type.GetGenericArguments());
                
                return new DependencyResolutionLocatorKey(mappedType, pair.ID);
            }
            else
            {
                return pair;
            }
        }
    }
    
    public class GenericTypeMappingStrategy : BuilderStrategy
    {
        public override object BuildUp(IBuilderContext context, Type t, object existing, string id)
        {
            DependencyResolutionLocatorKey result = new DependencyResolutionLocatorKey(t, id);
 
            ITypeMappingPolicy policy = null;
            if (t.IsGenericType)
            {
                Type genericType = t.GetGenericTypeDefinition();
                policy = context.Policies.Get<ITypeMappingPolicy>(genericType, id);
            }
            else
            {
                policy = context.Policies.Get<ITypeMappingPolicy>(t, id);
            }
 
            if (policy != null)
            {
                result = policy.Map(result);
                //TraceBuildUp(context, t, id, Properties.Resources.TypeMapped, result.Type, result.ID ?? "(null)");
                //Guard.TypeIsAssignableFromType(t, result.Type, t);
            }
 
            return base.BuildUp(context, result.Type, existing, result.ID);
        }
    }

I just modified the existing TypeMappingStrategy and TypeMappingPolicy to handle the generic types.

In order to use the new strategy in your container/builder, you'll need to configure your own builder. The easiest way to do this is by inheriting from BuilderBase<T>. See ObjectBuilder\Builder.cs for an example. Replace the usage of TypeMappingStrategy with GenericTypeMappingStrategy in your own copy of that code. I can post the code I use for my container if you need, it's pretty basic.

My container has a method AddTypeMapping(Type type, Type concreteType) and a method Get<T>() which gets an instance of the given type.

I can add the generic type mapping like so:
container.AddTypeMapping(typeof(IList<>), typeof(List<>));

and create an instance of the generic type:
// will return a List<string> instance
IList<string> testList = container.Get<IList<string>>();

Jan 18, 2008 at 3:17 PM
I am very new to the ObjectBuilder and am having issues with NTiers and SCSF. I am getting the following error when trying to load a view and I assume that it has to do with what this thread is talking about:

System.ArgumentException was unhandled
Message="Can not create an instance of type 'Hearn.Dispatcher.UserAccountModule.IEditUserAccountView'."
Source="Microsoft.Practices.ObjectBuilder"
StackTrace:
at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUpNewObject(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.SingletonStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.TypeMappingStrategy.BuildUp(IBuilderContext context, Type t, Object existing, String id)
at Microsoft.Practices.ObjectBuilder.BuilderBase`1.DoBuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies)
at Microsoft.Practices.ObjectBuilder.BuilderBase`1.BuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies)


Do do your solution do I have to open up the ObjectBuilder class and make changes there, or is there a way to get this to work without changing the ObjectBuilder source code? Again this is very new to me so I apologize if I am way off here. I am using the May 2007 version of the SCSF

Jason
Jan 18, 2008 at 3:31 PM


jmaronge wrote:
I am very new to the ObjectBuilder and am having issues with NTiers and SCSF. I am getting the following error when trying to load a view and I assume that it has to do with what this thread is talking about:

System.ArgumentException was unhandled
Message="Can not create an instance of type 'Hearn.Dispatcher.UserAccountModule.IEditUserAccountView'."
Source="Microsoft.Practices.ObjectBuilder"
StackTrace:
at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUpNewObject(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.CreationStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.ReflectionStrategy`1.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.SingletonStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild)
at Microsoft.Practices.ObjectBuilder.TypeMappingStrategy.BuildUp(IBuilderContext context, Type t, Object existing, String id)
at Microsoft.Practices.ObjectBuilder.BuilderBase`1.DoBuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies)
at Microsoft.Practices.ObjectBuilder.BuilderBase`1.BuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies)


Do do your solution do I have to open up the ObjectBuilder class and make changes there, or is there a way to get this to work without changing the ObjectBuilder source code? Again this is very new to me so I apologize if I am way off here. I am using the May 2007 version of the SCSF

Jason



Ignore my post, I found the problem. I was passing my Interface into the WorkItem.SmartParts.AddNew method instead of my class. I hate it when I do stupid stuff like that. Sorry for being such a dummy.....

Jason