1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Configuration
;
20 using System
.Reflection
;
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
;
36 /// Provides a basic level of integration with the NHibernate project
39 /// This facility allows components to gain access to the NHibernate's
41 /// <list type="bullet">
42 /// <item><description>NHibernate.Cfg.Configuration</description></item>
43 /// <item><description>NHibernate.ISessionFactory</description></item>
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.
52 /// <example>The following sample illustrates how a component
53 /// can access the session.
55 /// public class MyDao
57 /// private ISessionManager sessionManager;
59 /// public MyDao(ISessionManager sessionManager)
61 /// this.sessionManager = sessionManager;
64 /// public void Save(Data data)
66 /// using(ISession session = sessionManager.OpenSession())
68 /// session.Save(data);
74 public class NHibernateFacility
: AbstractFacility
76 private const String nHMappingAttributesAssemblyName
= "NHibernate.Mapping.Attributes";
79 /// The custom initialization for the Facility.
81 /// <remarks>It must be overriden.</remarks>
82 protected override void Init()
86 AssertHasAtLeastOneFactoryConfigured();
93 #region Set up of components
96 /// Registers the session factory resolver, the session store, the session manager and the transaction manager.
98 protected virtual void RegisterComponents()
100 RegisterSessionFactoryResolver();
101 RegisterSessionStore();
102 RegisterSessionManager();
103 RegisterTransactionManager();
107 /// Registers <see cref="SessionFactoryResolver"/> as the session factory resolver.
109 protected void RegisterSessionFactoryResolver()
111 Kernel
.AddComponent( "nhfacility.sessionfactory.resolver",
112 typeof(ISessionFactoryResolver
), typeof(SessionFactoryResolver
) );
116 /// Registers the configured session store.
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
);
153 /// Registers <see cref="DefaultSessionManager"/> as the session manager.
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
) );
176 /// Registers <see cref="DefaultTransactionManager"/> as the transaction manager.
178 protected void RegisterTransactionManager()
180 Kernel
.AddComponent( "nhibernate.transaction.manager",
181 typeof(ITransactionManager
), typeof(DefaultTransactionManager
));
186 #region Configuration methods
189 /// Configures the facility.
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;
216 /// Reads the attribute <c>useReflectionOptimizer</c> and configure
217 /// the reflection optimizer accordingly.
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
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
);
302 /// Applies the configuration settings.
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);
320 /// Registers the resources.
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
));
339 cfg
.AddXmlFile( Path
.Combine( AppDomain
.CurrentDomain
.BaseDirectory
, name
) );
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
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.
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) }
));
381 /// Registers the assemblies.
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
);
401 #region Helper methods
403 private Assembly
ObtainAssembly(String assembly
)
407 return Assembly
.Load(assembly
);
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
);