Fixed InversionOfControl/Castle.MicroKernel/Castle.MicroKernel-vs2008.csproj
[castle.git] / Facilities / NHibernateIntegration / Castle.Facilities.NHibernateIntegration / NHibernateFacility.cs
blob7b7554f26e4a7ad1ac4f021e3ca3f128f81d2517
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.Facilities.NHibernateIntegration
17 using System;
18 using System.Configuration;
19 using System.IO;
20 using System.Reflection;
22 using NHibernate;
23 using Configuration = NHibernate.Cfg.Configuration;
25 using Castle.Core.Configuration;
27 using Castle.MicroKernel;
28 using Castle.MicroKernel.Facilities;
29 using Castle.MicroKernel.SubSystems.Conversion;
31 using Castle.Services.Transaction;
33 using Castle.Facilities.NHibernateIntegration.Internal;
35 /// <summary>
36 /// Provides a basic level of integration with the NHibernate project
37 /// </summary>
38 /// <remarks>
39 /// This facility allows components to gain access to the NHibernate's
40 /// objects:
41 /// <list type="bullet">
42 /// <item><description>NHibernate.Cfg.Configuration</description></item>
43 /// <item><description>NHibernate.ISessionFactory</description></item>
44 /// </list>
45 /// <para>
46 /// It also allow you to obtain the ISession instance
47 /// through the component <see cref="ISessionManager"/>, which is
48 /// transaction aware and save you the burden of sharing session
49 /// or using a singleton.
50 /// </para>
51 /// </remarks>
52 /// <example>The following sample illustrates how a component
53 /// can access the session.
54 /// <code>
55 /// public class MyDao
56 /// {
57 /// private ISessionManager sessionManager;
58 ///
59 /// public MyDao(ISessionManager sessionManager)
60 /// {
61 /// this.sessionManager = sessionManager;
62 /// }
63 ///
64 /// public void Save(Data data)
65 /// {
66 /// using(ISession session = sessionManager.OpenSession())
67 /// {
68 /// session.Save(data);
69 /// }
70 /// }
71 /// }
72 /// </code>
73 /// </example>
74 public class NHibernateFacility : AbstractFacility
76 private const String nHMappingAttributesAssemblyName = "NHibernate.Mapping.Attributes";
78 /// <summary>
79 /// The custom initialization for the Facility.
80 /// </summary>
81 /// <remarks>It must be overriden.</remarks>
82 protected override void Init()
84 AssertHasConfig();
86 AssertHasAtLeastOneFactoryConfigured();
88 RegisterComponents();
90 ConfigureFacility();
93 #region Set up of components
95 /// <summary>
96 /// Registers the session factory resolver, the session store, the session manager and the transaction manager.
97 /// </summary>
98 protected virtual void RegisterComponents()
100 RegisterSessionFactoryResolver();
101 RegisterSessionStore();
102 RegisterSessionManager();
103 RegisterTransactionManager();
106 /// <summary>
107 /// Registers <see cref="SessionFactoryResolver"/> as the session factory resolver.
108 /// </summary>
109 protected void RegisterSessionFactoryResolver()
111 Kernel.AddComponent( "nhfacility.sessionfactory.resolver",
112 typeof(ISessionFactoryResolver), typeof(SessionFactoryResolver) );
115 /// <summary>
116 /// Registers the configured session store.
117 /// </summary>
118 protected void RegisterSessionStore()
120 String isWeb = FacilityConfig.Attributes["isWeb"];
121 String customStore = FacilityConfig.Attributes["customStore"];
123 // Default implementation
124 Type sessionStoreType = typeof(CallContextSessionStore);
126 if ("true".Equals(isWeb))
128 sessionStoreType = typeof(WebSessionStore);
131 if (customStore != null)
133 ITypeConverter converter = (ITypeConverter)
134 Kernel.GetSubSystem( SubSystemConstants.ConversionManagerKey );
136 sessionStoreType = (Type)
137 converter.PerformConversion( customStore, typeof(Type) );
139 if (!typeof(ISessionStore).IsAssignableFrom(sessionStoreType))
141 String message = "The specified customStore does " +
142 "not implement the interface ISessionStore. Type " + customStore;
144 throw new ConfigurationErrorsException(message);
148 Kernel.AddComponent( "nhfacility.sessionstore",
149 typeof(ISessionStore), sessionStoreType );
152 /// <summary>
153 /// Registers <see cref="DefaultSessionManager"/> as the session manager.
154 /// </summary>
155 protected void RegisterSessionManager()
157 string defaultFlushMode = FacilityConfig.Attributes["defaultFlushMode"];
159 if (defaultFlushMode != null && defaultFlushMode != string.Empty)
161 MutableConfiguration confignode = new MutableConfiguration("nhfacility.sessionmanager");
163 IConfiguration properties =
164 confignode.Children.Add(new MutableConfiguration("parameters"));
166 properties.Children.Add(new MutableConfiguration("DefaultFlushMode", defaultFlushMode));
168 Kernel.ConfigurationStore.AddComponentConfiguration("nhfacility.sessionmanager", confignode);
171 Kernel.AddComponent( "nhfacility.sessionmanager",
172 typeof(ISessionManager), typeof(DefaultSessionManager) );
175 /// <summary>
176 /// Registers <see cref="DefaultTransactionManager"/> as the transaction manager.
177 /// </summary>
178 protected void RegisterTransactionManager()
180 Kernel.AddComponent( "nhibernate.transaction.manager",
181 typeof(ITransactionManager), typeof(DefaultTransactionManager));
184 #endregion
186 #region Configuration methods
188 /// <summary>
189 /// Configures the facility.
190 /// </summary>
191 protected void ConfigureFacility()
193 ISessionFactoryResolver sessionFactoryResolver = (ISessionFactoryResolver)
194 Kernel[typeof(ISessionFactoryResolver)];
196 ConfigureReflectionOptimizer(FacilityConfig);
198 bool firstFactory = true;
200 foreach(IConfiguration factoryConfig in FacilityConfig.Children)
202 if (!"factory".Equals(factoryConfig.Name))
204 String message = "Unexpected node " + factoryConfig.Name;
206 throw new ConfigurationErrorsException(message);
209 ConfigureFactories(factoryConfig, sessionFactoryResolver, firstFactory);
211 firstFactory = false;
215 /// <summary>
216 /// Reads the attribute <c>useReflectionOptimizer</c> and configure
217 /// the reflection optimizer accordingly.
218 /// </summary>
219 /// <remarks>
220 /// As reported on Jira (FACILITIES-39) the reflection optimizer
221 /// slow things down. So by default it will be disabled. You
222 /// can use the attribute <c>useReflectionOptimizer</c> to turn it
223 /// on.
224 /// </remarks>
225 /// <param name="config"></param>
226 private void ConfigureReflectionOptimizer(IConfiguration config)
228 ITypeConverter converter = (ITypeConverter)
229 Kernel.GetSubSystem( SubSystemConstants.ConversionManagerKey );
231 String useReflOptAtt = config.Attributes["useReflectionOptimizer"];
233 bool useReflectionOptimizer = false;
235 if (useReflOptAtt != null)
237 useReflectionOptimizer = (bool)
238 converter.PerformConversion(useReflOptAtt, typeof(bool));
241 NHibernate.Cfg.Environment.UseReflectionOptimizer = useReflectionOptimizer;
244 private void ConfigureFactories(IConfiguration config,
245 ISessionFactoryResolver sessionFactoryResolver, bool firstFactory)
247 String id = config.Attributes["id"];
249 if (id == null || String.Empty.Equals(id))
251 String message = "You must provide a " +
252 "valid 'id' attribute for the 'factory' node. This id is used as key for " +
253 "the ISessionFactory component registered on the container";
255 throw new ConfigurationErrorsException(message);
258 String alias = config.Attributes["alias"];
260 if (!firstFactory && (alias == null || alias.Length == 0))
262 String message = "You must provide a " +
263 "valid 'alias' attribute for the 'factory' node. This id is used to obtain " +
264 "the ISession implementation from the SessionManager";
266 throw new ConfigurationErrorsException(message);
268 else if (alias == null || alias.Length == 0)
270 alias = Constants.DefaultAlias;
273 Configuration cfg = new Configuration();
275 ApplyConfigurationSettings(cfg, config.Children["settings"]);
276 RegisterAssemblies(cfg, config.Children["assemblies"]);
277 RegisterResources(cfg, config.Children["resources"]);
279 // Registers the Configuration object
281 Kernel.AddComponentInstance( String.Format("{0}.cfg", id), cfg );
283 // If a Session Factory level interceptor was provided, we use it
285 if (Kernel.HasComponent("nhibernate.sessionfactory.interceptor"))
287 cfg.Interceptor = (IInterceptor) Kernel["nhibernate.sessionfactory.interceptor"];
290 // Registers the ISessionFactory as a component
292 ISessionFactory sessionFactory = cfg.BuildSessionFactory();
294 Kernel.AddComponentInstance( id, typeof(ISessionFactory), sessionFactory );
296 // Registers the ISessionFactory within the ISessionFactoryResolver
298 sessionFactoryResolver.RegisterAliasComponentIdMapping(alias, id);
301 /// <summary>
302 /// Applies the configuration settings.
303 /// </summary>
304 /// <param name="cfg">The CFG.</param>
305 /// <param name="facilityConfig">The facility config.</param>
306 protected void ApplyConfigurationSettings(Configuration cfg, IConfiguration facilityConfig)
308 if (facilityConfig == null) return;
310 foreach(IConfiguration item in facilityConfig.Children)
312 String key = item.Attributes["key"];
313 String value = item.Value;
315 cfg.SetProperty(key, value);
319 /// <summary>
320 /// Registers the resources.
321 /// </summary>
322 /// <param name="cfg">The CFG.</param>
323 /// <param name="facilityConfig">The facility config.</param>
324 protected void RegisterResources(Configuration cfg, IConfiguration facilityConfig)
326 if (facilityConfig == null) return;
328 foreach(IConfiguration item in facilityConfig.Children)
330 String name = item.Attributes["name"];
331 String assembly = item.Attributes["assembly"];
333 if (assembly != null)
335 cfg.AddResource(name, ObtainAssembly(assembly));
337 else
339 cfg.AddXmlFile( Path.Combine( AppDomain.CurrentDomain.BaseDirectory, name ) );
344 /// <summary>
345 /// If <paramref name="targetAssembly"/> has a reference on
346 /// <c>NHibernate.Mapping.Attributes</c> : use the NHibernate mapping
347 /// attributes contained in that assembly to update NHibernate
348 /// configuration (<paramref name="cfg"/>). Else do nothing
349 /// </summary>
350 /// <remarks>
351 /// To avoid an unnecessary dependency on the library
352 /// <c>NHibernate.Mapping.Attributes.dll</c> when using this
353 /// facility without NHibernate mapping attributes, all calls to that
354 /// library are made using reflexion.
355 /// </remarks>
356 /// <param name="cfg">NHibernate configuration</param>
357 /// <param name="targetAssembly">Target assembly name</param>
358 protected void GenerateMappingFromAttributesIfNeeded(Configuration cfg, String targetAssembly)
360 //Get an array of all assemblies referenced by targetAssembly
361 AssemblyName[] refAssemblies = Assembly.Load(targetAssembly).GetReferencedAssemblies();
363 //If assembly "NHibernate.Mapping.Attributes" is referenced in targetAssembly
364 if (Array.Exists<AssemblyName>(refAssemblies, delegate(AssemblyName an) { return an.Name.Equals(nHMappingAttributesAssemblyName); }))
366 //Obtains, by reflexion, the necessary tools to generate NH mapping from attributes
367 Type HbmSerializerType = Type.GetType(String.Concat(nHMappingAttributesAssemblyName, ".HbmSerializer, ", nHMappingAttributesAssemblyName));
368 Object hbmSerializer = Activator.CreateInstance(HbmSerializerType);
369 PropertyInfo validate = HbmSerializerType.GetProperty("Validate");
370 MethodInfo serialize = HbmSerializerType.GetMethod("Serialize", new Type[] { typeof(Assembly) });
372 //Enable validation of mapping documents generated from the mapping attributes
373 validate.SetValue(hbmSerializer, true, null);
375 //Generates a stream of mapping documents from all decorated classes in targetAssembly and add it to NH config
376 cfg.AddInputStream((MemoryStream)serialize.Invoke(hbmSerializer, new object[] { Assembly.Load(targetAssembly) }));
380 /// <summary>
381 /// Registers the assemblies.
382 /// </summary>
383 /// <param name="cfg">The CFG.</param>
384 /// <param name="facilityConfig">The facility config.</param>
385 protected void RegisterAssemblies(Configuration cfg, IConfiguration facilityConfig)
387 if (facilityConfig == null) return;
389 foreach(IConfiguration item in facilityConfig.Children)
391 String assembly = item.Value;
393 cfg.AddAssembly(assembly);
395 GenerateMappingFromAttributesIfNeeded(cfg, assembly);
399 #endregion
401 #region Helper methods
403 private Assembly ObtainAssembly(String assembly)
407 return Assembly.Load(assembly);
409 catch(Exception ex)
411 String message = String.Format("The assembly {0} could not be loaded.", assembly);
413 throw new ConfigurationErrorsException(message, ex);
417 private void AssertHasAtLeastOneFactoryConfigured()
419 IConfiguration factoriesConfig = FacilityConfig.Children["factory"];
421 if (factoriesConfig == null)
423 String message = "You need to configure at least one factory to use the NHibernateFacility";
425 throw new ConfigurationErrorsException(message);
429 private void AssertHasConfig()
431 if (FacilityConfig == null)
433 String message = "The NHibernateFacility requires an external configuration";
435 throw new ConfigurationErrorsException(message);
439 #endregion