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
.ActiveRecord
.Framework
18 using System
.Threading
;
19 using System
.Collections
;
20 using System
.Runtime
.CompilerServices
;
21 using Iesi
.Collections
;
24 using Castle
.ActiveRecord
.Framework
.Scopes
;
27 /// Default implementation of <seealso cref="ISessionFactoryHolder"/>
30 /// This class is thread safe
32 public class SessionFactoryHolder
: MarshalByRefObject
, ISessionFactoryHolder
34 private readonly Hashtable type2Conf
= Hashtable
.Synchronized(new Hashtable());
35 private readonly Hashtable type2SessFactory
= Hashtable
.Synchronized(new Hashtable());
36 private readonly ReaderWriterLock readerWriterLock
= new ReaderWriterLock();
37 private IThreadScopeInfo threadScopeInfo
;
40 /// Raised when a root type is registered.
42 public event RootTypeHandler OnRootTypeRegistered
;
45 /// Associates a Configuration object to a root type
47 /// <param name="rootType"></param>
48 /// <param name="cfg"></param>
49 public void Register(Type rootType
, Configuration cfg
)
51 type2Conf
.Add(rootType
, cfg
);
53 if (OnRootTypeRegistered
!= null)
55 OnRootTypeRegistered(this, rootType
);
60 /// Requests the Configuration associated to the type.
62 public Configuration
GetConfiguration(Type type
)
64 return type2Conf
[type
] as Configuration
;
70 public Configuration
[] GetAllConfigurations()
72 HashedSet
set = new HashedSet(type2Conf
.Values
);
74 Configuration
[] confs
= new Configuration
[set.Count
];
82 /// Optimized with reader/writer lock.
84 /// <param name="type"></param>
85 /// <returns></returns>
86 public ISessionFactory
GetSessionFactory(Type type
)
88 Type normalizedtype
= GetRootType(type
);
90 if (normalizedtype
== null)
92 throw new ActiveRecordException("No configuration for ActiveRecord found in the type hierarchy -> " + type
.FullName
);
95 readerWriterLock
.AcquireReaderLock(-1);
99 ISessionFactory sessFactory
= type2SessFactory
[normalizedtype
] as ISessionFactory
;
101 if (sessFactory
!= null)
106 LockCookie lc
= readerWriterLock
.UpgradeToWriterLock(-1);
110 sessFactory
= type2SessFactory
[normalizedtype
] as ISessionFactory
;
112 if (sessFactory
!= null)
116 Configuration cfg
= GetConfiguration(normalizedtype
);
118 sessFactory
= cfg
.BuildSessionFactory();
120 type2SessFactory
[normalizedtype
] = sessFactory
;
126 readerWriterLock
.DowngradeFromWriterLock(ref lc
);
131 readerWriterLock
.ReleaseReaderLock();
136 /// This method allows direct registration
137 /// of a session factory to a type, bypassing the normal preperation that AR
139 /// The main usage is in testing, so you would be able to switch the session factory
141 /// Note that this will override the current session factory for the baseType.
143 public void RegisterSessionFactory(ISessionFactory sessionFactory
, Type baseType
)
145 type2SessFactory
[baseType
] = sessionFactory
;
149 /// Creates a session for the associated type
151 [MethodImpl(MethodImplOptions
.Synchronized
)]
152 public ISession
CreateSession(Type type
)
154 if (threadScopeInfo
.HasInitializedScope
)
156 return CreateScopeSession(type
);
159 ISessionFactory sessionFactory
= GetSessionFactory(type
);
161 ISession session
= OpenSession(sessionFactory
);
167 /// Gets the type of the root.
169 /// <param name="type">The type.</param>
170 /// <returns></returns>
171 public Type
GetRootType(Type type
)
173 while(type
!= typeof(object))
175 if (type2Conf
.ContainsKey(type
))
180 type
= type
.BaseType
;
182 //to enable multiple database support for generic types
183 if (type
.IsGenericType
)
185 Type genericTypeDef
= type
.GetGenericTypeDefinition();
187 if (type2Conf
.ContainsKey(genericTypeDef
))
189 return genericTypeDef
;
194 return typeof(ActiveRecordBase
);
197 private static ISession
OpenSession(ISessionFactory sessionFactory
)
201 return sessionFactory
.OpenSession(InterceptorFactory
.Create());
205 private static ISession
OpenSessionWithScope(ISessionScope scope
, ISessionFactory sessionFactory
)
209 return scope
.OpenSession(sessionFactory
, InterceptorFactory
.Create());
214 /// Releases the specified session
216 /// <param name="session"></param>
217 public void ReleaseSession(ISession session
)
219 if (!threadScopeInfo
.HasInitializedScope
)
227 /// Called if an action on the session fails
229 /// <param name="session"></param>
230 public void FailSession(ISession session
)
232 if (threadScopeInfo
.HasInitializedScope
)
234 ISessionScope scope
= threadScopeInfo
.GetRegisteredScope();
235 scope
.FailSession(session
);
244 /// Gets or sets the implementation of <see cref="IThreadScopeInfo"/>
247 public IThreadScopeInfo ThreadScopeInfo
249 get { return threadScopeInfo; }
252 ThreadScopeAccessor
.Instance
.ScopeInfo
= value;
253 threadScopeInfo
= value;
257 private ISession
CreateScopeSession(Type type
)
259 ISessionScope scope
= threadScopeInfo
.GetRegisteredScope();
260 ISessionFactory sessionFactory
= GetSessionFactory(type
);
262 System
.Diagnostics
.Debug
.Assert(scope
!= null);
263 System
.Diagnostics
.Debug
.Assert(sessionFactory
!= null);
265 if (scope
.IsKeyKnown(sessionFactory
))
267 return scope
.GetSession(sessionFactory
);
273 if (scope
.WantsToCreateTheSession
)
275 session
= OpenSessionWithScope(scope
, sessionFactory
);
279 session
= OpenSession(sessionFactory
);
282 System
.Diagnostics
.Debug
.Assert(session
!= null);
284 scope
.RegisterSession(sessionFactory
, session
);