PageRenderTime 25ms CodeModel.GetById 17ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/src/LinFu.Proxy/ProxyFactory.cs

http://github.com/philiplaureano/LinFu
C# | 191 lines | 103 code | 38 blank | 50 comment | 20 complexity | f1d49e5a2acbec1b4deb236beb7e794c MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Reflection;
  5using LinFu.AOP.Cecil.Interfaces;
  6using LinFu.IoC.Configuration;
  7using LinFu.IoC.Interfaces;
  8using LinFu.Proxy.Interfaces;
  9using LinFu.Reflection.Emit;
 10using Mono.Cecil;
 11using TypeAttributes = Mono.Cecil.TypeAttributes;
 12
 13namespace LinFu.Proxy
 14{
 15    /// <summary>
 16    ///     Provides the basic implementation for a proxy factory class.
 17    /// </summary>
 18    [Implements(typeof(IProxyFactory), LifecycleType.OncePerRequest)]
 19    public class ProxyFactory : IProxyFactory, IInitialize
 20    {
 21        /// <summary>
 22        ///     Initializes the proxy factory with the default values.
 23        /// </summary>
 24        public ProxyFactory()
 25        {
 26            // Use the forwarding proxy type by default
 27            ProxyBuilder = new SerializableProxyBuilder();
 28            InterfaceExtractor = new InterfaceExtractor();
 29            Cache = new ProxyCache();
 30        }
 31
 32        /// <summary>
 33        ///     Gets or sets the <see cref="IExtractInterfaces" /> type that will be
 34        ///     responsible for determining which interfaces
 35        ///     the proxy type should implement.
 36        /// </summary>
 37        public IExtractInterfaces InterfaceExtractor { get; set; }
 38
 39        /// <summary>
 40        ///     The <see cref="IProxyBuilder" /> instance that is
 41        ///     responsible for generating the proxy type.
 42        /// </summary>
 43        public IProxyBuilder ProxyBuilder { get; set; }
 44
 45        /// <summary>
 46        ///     The <see cref="IVerifier" /> instance that will be used to
 47        ///     ensure that the generated assemblies are valid.
 48        /// </summary>
 49        public IVerifier Verifier { get; set; }
 50
 51        /// <summary>
 52        ///     Gets or sets a value indicating the <see cref="IProxyCache" />
 53        ///     instance that will be used to cache previous proxy generation runs.
 54        /// </summary>
 55        public IProxyCache Cache { get; set; }
 56
 57
 58        /// <summary>
 59        ///     Initializes the <see cref="ProxyFactory" /> instance
 60        ///     with the <paramref name="source" /> container.
 61        /// </summary>
 62        /// <param name="source">The <see cref="IServiceContainer" /> instance that will hold the ProxyFactory.</param>
 63        public virtual void Initialize(IServiceContainer source)
 64        {
 65            if (source.Contains(typeof(IProxyBuilder), new Type[0]))
 66                ProxyBuilder = (IProxyBuilder) source.GetService(typeof(IProxyBuilder));
 67
 68            if (source.Contains(typeof(IExtractInterfaces), new Type[0]))
 69                InterfaceExtractor = (IExtractInterfaces) source.GetService(typeof(IExtractInterfaces));
 70
 71            //if (source.Contains(typeof(IVerifier)))
 72            //    Verifier = source.GetService<IVerifier>();
 73
 74            if (source.Contains(typeof(IProxyCache), new Type[0]))
 75                Cache = (IProxyCache) source.GetService(typeof(IProxyCache));
 76        }
 77
 78
 79        /// <summary>
 80        ///     Creates a proxy type using the given
 81        ///     <paramref name="baseType" /> as the base class
 82        ///     and ensures that the proxy type implements the given
 83        ///     interface types.
 84        /// </summary>
 85        /// <param name="baseType">The base class from which the proxy type will be derived.</param>
 86        /// <param name="baseInterfaces">The list of interfaces that the proxy will implement.</param>
 87        /// <returns>A forwarding proxy.</returns>
 88        public Type CreateProxyType(Type baseType, IEnumerable<Type> baseInterfaces)
 89        {
 90            // Reuse the cached results, if possible
 91            var originalInterfaces = baseInterfaces.ToArray();
 92            if (Cache != null && Cache.Contains(baseType, originalInterfaces))
 93                return Cache.Get(baseType, originalInterfaces);
 94
 95            if (!baseType.IsPublic)
 96                throw new ArgumentException("The proxy factory can only generate proxies from public base classes.",
 97                    "baseType");
 98
 99            var hasNonPublicInterfaces = (from t in baseInterfaces
100                                             where t.IsNotPublic
101                                             select t).Count() > 0;
102
103            if (hasNonPublicInterfaces)
104                throw new ArgumentException("The proxy factory cannot generate proxies from non-public interfaces.",
105                    "baseInterfaces");
106
107
108            var actualBaseType = baseType.IsInterface ? typeof(object) : baseType;
109            var interfaces = new HashSet<Type>(baseInterfaces);
110            // Move the base type into the list of interfaces
111            // if the user mistakenly entered
112            // an interface type as the base type
113            if (baseType.IsInterface) interfaces.Add(baseType);
114
115            if (InterfaceExtractor != null)
116            {
117                // Get the interfaces for the base type
118                InterfaceExtractor.GetInterfaces(actualBaseType, interfaces);
119
120                var targetList = interfaces.ToArray();
121                // Extract the inherited interfaces
122                foreach (var type in targetList) InterfaceExtractor.GetInterfaces(type, interfaces);
123            }
124
125
126            var assemblyName = "LinFu.Proxy";
127            var assembly = AssemblyDefinition.CreateAssembly(
128                new AssemblyNameDefinition(assemblyName, new Version(1, 0)),
129                $"anonymousmodule-{Guid.NewGuid().ToString()}", ModuleKind.Dll);
130            var mainModule = assembly.MainModule;
131            var importedBaseType = mainModule.Import(actualBaseType);
132            var attributes = TypeAttributes.AutoClass | TypeAttributes.Class |
133                             TypeAttributes.Public | TypeAttributes.BeforeFieldInit;
134
135
136            var guid = Guid.NewGuid().ToString().Replace("-", "");
137            var typeName = string.Format("{0}Proxy-{1}", baseType.Name, guid);
138            var namespaceName = "LinFu.Proxy";
139            var proxyType = mainModule.DefineClass(typeName, namespaceName,
140                attributes, importedBaseType);
141
142            proxyType.AddDefaultConstructor();
143
144
145            if (ProxyBuilder == null)
146                throw new NullReferenceException("The 'ProxyBuilder' property cannot be null");
147
148            // Add the list of interfaces to the target type
149            foreach (var interfaceType in interfaces)
150            {
151                if (!interfaceType.IsInterface)
152                    continue;
153
154                var currentType = mainModule.Import(interfaceType);
155                proxyType.Interfaces.Add(currentType);
156            }
157
158            // Hand it off to the builder for construction
159            ProxyBuilder.Construct(actualBaseType, interfaces, mainModule, proxyType);
160
161            // Verify the assembly, if possible
162            if (Verifier != null)
163                Verifier.Verify(assembly);
164
165
166            var compiledAssembly = assembly.ToAssembly();
167
168            IEnumerable<Type> types = null;
169
170            try
171            {
172                types = compiledAssembly.GetTypes();
173            }
174            catch (ReflectionTypeLoadException ex)
175            {
176                types = ex.Types;
177            }
178
179            var result = (from t in types
180                where t != null && t.IsClass
181                select t).FirstOrDefault();
182
183
184            // Cache the result
185            if (Cache != null)
186                Cache.Store(result, baseType, originalInterfaces);
187
188            return result;
189        }
190    }
191}