/src/LinFu.IoC/Configuration/Loaders/FactoryAttributeLoader.cs

1﻿usingSystem; 2usingSystem.Collections.Generic; 3usingSystem.IO; 4usingSystem.Linq; 5usingLinFu.IoC.Configuration.Injectors; 6usingLinFu.IoC.Factories; 7usingLinFu.IoC.Interfaces; 8 9namespaceLinFu.IoC.Configuration 10{ 11/// <summary>
12/// A class that injects custom <see cref="IFactory" /> and <see cref="IFactory{T}" />
13/// instances into an <see cref="IServiceContainer" /> instance.
14/// </summary>
15publicclassFactoryAttributeLoader:ITypeLoader 16{ 17/// <summary>
18/// Loads an <see cref="IFactory" /> and <see cref="IFactory{T}" /> instance
19/// into a <see cref="IServiceContainer" /> instance using the given
20/// <paramref name="sourceType" />.
21/// </summary>
22/// <param name="sourceType">The input type from which one or more factories will be created.</param>
23/// <returns>A set of <see cref="Action{T}" /> instances. This cannot be null.</returns>
24publicIEnumerable<Action<IServiceContainer>>Load(TypesourceType) 25{ 26if(sourceType==null) 27thrownewArgumentNullException("sourceType"); 28 29// Extract the factory attributes from the current type
30varattributes=sourceType.GetCustomAttributes(typeof(FactoryAttribute),false); 31varattributeList=attributes.Cast<FactoryAttribute>() 32.Where(f=>f!=null).ToList(); 33 34 35// The target type must have at least one
36// factory attribute
37if(attributeList.Count==0) 38returnnewAction<IServiceContainer>[0]; 39 40 41// Make sure the factory is created only once
42Func<IFactoryRequest,object>getFactoryInstance=request=> 43{ 44varcontainer=request.Container; 45returncontainer.AutoCreateInternal(sourceType); 46}; 47 48 49returnGetResults(sourceType,attributeList,getFactoryInstance); 50} 51 52/// <summary>
53/// Determines whether or not the current <paramref name="sourceType" />
54/// can be loaded.
55/// </summary>
56/// <param name="sourceType">The source type currently being loaded.</param>
57/// <returns>Returns <c>true</c> if the type is a class type; otherwise, it returns <c>false</c>.</returns>
58publicboolCanLoad(TypesourceType) 59{ 60try 61{ 62returnsourceType.IsClass; 63} 64catch(TypeInitializationException) 65{ 66// Ignore the error
67returnfalse; 68} 69catch(FileNotFoundException) 70{ 71// Ignore the error
72returnfalse; 73} 74} 75 76 77/// <summary>
78/// Instantiates the <see cref="IFactory" /> instances associated with the <paramref name="sourceType" /> and
79/// adds those factories to the target container upon initialization.
80/// </summary>
81/// <param name="sourceType">The <see cref="System.Type" /> currently being inspected.</param>
82/// <param name="attributeList">
83/// The list of <see cref="FactoryAttribute" /> instances currently declared on on the source
84/// type.
85/// </param>
86/// <param name="getFactoryInstance">The functor that will be responsible for generating the factory instance.</param>
87/// <returns>A list of actions that will add the factories to the target container.</returns>
88privatestaticIEnumerable<Action<IServiceContainer>>GetResults(TypesourceType, 89IEnumerable<FactoryAttribute>attributeList, 90Func<IFactoryRequest,object> 91getFactoryInstance) 92{ 93varresults=newList<Action<IServiceContainer>>(); 94// The factory instance must implement either
95// IFactory or IFactory<T>
96varfactoryInterfaces=fromtinsourceType.GetInterfaces() 97wheret.IsGenericType&& 98t.GetGenericTypeDefinition()==typeof(IFactory<>) 99selectt;100101if(!typeof(IFactory).IsAssignableFrom(sourceType)&&factoryInterfaces.Count()==0)102{103varmessage=104string.Format(105"The factory type '{0}' must implement either the IFactory interface or the IFactory<T> interface.",106sourceType.AssemblyQualifiedName);107thrownewArgumentException(message,"sourceType");108}109110varimplementedInterfaces=newHashSet<Type>(factoryInterfaces);111Func<Type,Func<IFactoryRequest,object>,IFactory>createFactory=112(currentServiceType,getFactory)=>113{114// Determine if the factory implements
115// the generic IFactory<T> instance
116// and use that instance if possible
117118Func<IFactoryRequest,IFactory>getStronglyTypedFactory=119request=>120{121varresult=getFactory(request);122// If the object is IFactory then we can just return it.
123if(resultisIFactory)124return(IFactory)result;125126// Check to see if the object is IFactory<T>, if so we need to adapt it to
127// IFactory.
128vargenericType=typeof(IFactory<>).MakeGenericType(currentServiceType);129if(!genericType.IsInstanceOfType(result))returnnull;130131// Adapt IFactory<T> to IFactory.
132varadapterType=typeof(FactoryAdapter<>).MakeGenericType(currentServiceType);133varadapter=(IFactory)Activator.CreateInstance(adapterType,result);134returnadapter;135};136137returnGetFactory(currentServiceType,getStronglyTypedFactory,implementedInterfaces);138};139140// Build the list of services that this factory can implement
141varservicesToImplement=fromfinattributeList142letserviceName=f.ServiceName143letserviceType=f.ServiceType144letargumentTypes=f.ArgumentTypes??newType[0]145letfactory=createFactory(serviceType,getFactoryInstance)146wherefactory!=null147selectnew148{149ServiceName=serviceName,150ServiceType=serviceType,151ArgumentTypes=argumentTypes,152FactoryInstance=factory153};154155156foreach(varcurrentServiceinservicesToImplement)157{158varserviceName=currentService.ServiceName;159varserviceType=currentService.ServiceType;160varargumentTypes=currentService.ArgumentTypes;161varfactory=currentService.FactoryInstance;162163// HACK: Unnamed custom factories should be able to
164// intercept every request for the given service type
165if(serviceName==null)166{167varinjector=newCustomFactoryInjector(serviceType,factory);168results.Add(container=>container.PreProcessors.Add(injector));169}170171// Add each service to the container on initialization
172results.Add(container=>container.AddFactory(serviceName,serviceType,argumentTypes,factory));173}174175returnresults;176}177178/// <summary>
179/// Instantiates the given factory using the <paramref name="getStronglyTypedFactory">factory functor.</paramref>
180/// </summary>
181/// <param name="currentServiceType">The service type that will be created by the factory.</param>
182/// <param name="getStronglyTypedFactory">The functor that will be responsible for creating the factory itself.</param>
183/// <param name="implementedInterfaces">
184/// The list of <see cref="IFactory{T}" /> interfaces that are implemented by the
185/// source type.
186/// </param>
187/// <returns>A valid factory instance.</returns>
188privatestaticIFactoryGetFactory(TypecurrentServiceType,189Func<IFactoryRequest,IFactory>getStronglyTypedFactory,190ICollection<Type>implementedInterfaces)191{192vargenericType=typeof(IFactory<>).MakeGenericType(currentServiceType);193194// Lazy-instantiate the factories so that they can be injected by the container
195IFactorylazyFactory=newLazyFactory(getStronglyTypedFactory);196197IFactoryresult;198if(implementedInterfaces.Contains(genericType))199{200// Convert the IFactory<T> instance down to an IFactory
201// instance so that it can be used by the target container
202varlazyFactoryType=typeof(LazyFactory<>).MakeGenericType(currentServiceType);203varadapterType=typeof(FactoryAdapter<>).MakeGenericType(currentServiceType);204205lazyFactory=(IFactory)Activator.CreateInstance(lazyFactoryType,getStronglyTypedFactory);206result=(IFactory)Activator.CreateInstance(adapterType,lazyFactory);207208returnresult;209}210211// Otherwise, use the untyped IFactory instance instead
212result=lazyFactory;213returnresult;214}215}216}