--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-pro...@googlegroups.com.
To unsubscribe from this group, send email to castle-project-u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
public class WcfSessionPerRequestCallContextInitializer : ICallContextInitializer { private readonly ISessionManager sessionManager; private readonly string[] dbAliases; public WcfSessionPerRequestCallContextInitializer(ISessionManager sessionManager, string[] dbAliases) { this.sessionManager = sessionManager; this.dbAliases = dbAliases; } public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) { var extension = new NHibernateContextExtension(); foreach (var dbAlias in dbAliases) { extension.Sessions.Add(sessionManager.OpenSession(dbAlias)); } instanceContext.Extensions.Add(extension); return extension; } public void AfterInvoke(object correlationState) { if (correlationState != null) { ((IDisposable)correlationState).Dispose(); } } }
Rich
--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-pro...@googlegroups.com.
To unsubscribe from this group, send email to castle-project-u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
Hello,
Beware that for version 3 (which is btw not official from the powers that be), the API is different.
The aim of the new is to use IoC to a greater extent and hand-crafted code to a lesser.
The code that selects database looks like this:
Component.For<ISessionManager>().Instance(new SessionManager(() => {
var factory = Kernel.Resolve<ISessionFactory>(x.Instance.SessionFactoryKey);
var s = x.Instance.Interceptor.Do(y => factory.OpenSession(y)).OrDefault(factory.OpenSession());
s.FlushMode = _FlushMode;
return s;
}))
.Named(x.Instance.SessionFactoryKey + SessionManagerSuffix)
.LifeStyle.Singleton
So when you follow:
https://github.com/haf/Castle.Facilities.NHibernate/wiki/NHibernate-Facility---Quick-Start
and implement the INHibernateInstaller, then you can choose the session factory key.
When you then register your WcfSessionPerRequestCallContextInitializer, you can choose what ISessionManager instance you depend on.
If you choose to go the manual way with Transactions, beware that you handle the edge-cases well. Or even better; tell me what type of API you want to see for release version 3.0, which doesn’t use AOP, but allows you to call a ‘transaction handler’ of some sort.
Cheers
PS;
It’s actually version 0.2 of NHFac, 3.0.0.2007 of tx.
…and there’s a new concept which is default; session per transaction, which is what you are looking for (per operation as you say yourself)!
From: castle-pro...@googlegroups.com [mailto:castle-pro...@googlegroups.com] On Behalf Of Berke Sokhan
Sent: den 23 maj 2011 15:30
To: castle-pro...@googlegroups.com
Cc: hen...@haf.se
Subject: Re: NHibernateFacility and session-per-WCF-operation
Hello,
public class WcfSessionPerRequestBehavior : IServiceBehavior { private readonly ISessionManager sessionManager; private string[] dbAliases = { "nh.facility.default" }; public string[] DbAliases { get { return dbAliases; } set { dbAliases = value; } } public WcfSessionPerRequestBehavior(ISessionManager sessionManager) { this.sessionManager = sessionManager; } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (var cdb in serviceHostBase.ChannelDispatchers) { var channelDispatcher = cdb as ChannelDispatcher; if (null != channelDispatcher) { foreach (var endpointDispatcher in channelDispatcher.Endpoints) { foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations) { dispatchOperation.CallContextInitializers.Add(new WcfSessionPerRequestCallContextInitializer(sessionManager, dbAliases)); } } } } } }
.Register(Component.For<ICallContextInitializer>().ImplementedBy<WcfSessionPerRequestCallContextInitializer>().LifeStyle.PerWcfOperation())?
public class WcfSessionPerRequestCallContextInitializer : ICallContextInitializer { private readonly ISessionManager sessionManager; private readonly string[] dbAliases; public WcfSessionPerRequestCallContextInitializer(ISessionManager sessionManager, string[] dbAliases) { this.sessionManager = sessionManager; this.dbAliases = dbAliases; } public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) { var extension = new NHibernateContextExtension(); foreach (var dbAlias in dbAliases) { extension.Sessions.Add(sessionManager.OpenSession(dbAlias)); } instanceContext.Extensions.Add(extension); return extension; } public void AfterInvoke(object correlationState) { if (correlationState != null) { ((IDisposable)correlationState).Dispose(); } } }
public class WcfSessionPerRequestCallContextInitializer : ICallContextInitializer { private readonly ISessionManager
sessionManager; public WcfSessionPerRequestCallContextInitializer(ISessionManager sessionManager) { this.sessionManager = sessionManager; } public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) { var extension = new NHibernateContextExtension(); NHibernate.ISession session = sessionManager.OpenSession(); session.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted); extension.Sessions.Add(session); instanceContext.Extensions.Add(extension); return extension; } public void AfterInvoke(object correlationState) { if (correlationState != null) { ((IDisposable)correlationState).Dispose(); } } }
public class WcfSessionPerRequestBehavior : IServiceBehavior { private readonly ISessionManager
sessionManager; private readonly ICallContextInitializer callContextInitializer; public WcfSessionPerRequestBehavior(ISessionManager sessionManager, ICallContextInitializer callContextInitializer) { this.sessionManager = sessionManager; this.callContextInitializer = callContextInitializer; } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (var cdb in serviceHostBase.ChannelDispatchers) { var channelDispatcher = cdb as ChannelDispatcher; if (null != channelDispatcher) { foreach (var endpointDispatcher in channelDispatcher.Endpoints) { foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations) { dispatchOperation.CallContextInitializers.Add(callContextInitializer); } } } } } }So my last question would be, is it certain that ISessionManagers that will be injected to WcfSessionPerRequestBehavior and WcfSessionPerRequestCallContextInitializer are really different instances for "per wcf operation" (or session per trnx as you would say)?
public sealed class NHibernateContextExtension : IExtension<InstanceContext>, IDisposable { public List<ISession> Sessions { get; set; } public NHibernateContextExtension() { Sessions = new List<ISession>(); } public void Attach(InstanceContext owner) { } public void Detach(InstanceContext owner) { } public void Dispose() { if (Sessions != null) { Sessions.ForEach(s => s.Transaction.Commit()); Sessions.ForEach(s => s.Close()); } } }
public class Repository : IRepository { protected Func<ISession> sessionStrategy; public Repository(Func<ISession> sessionStrategy) { this.sessionStrategy = sessionStrategy; } public T GetById<T>(object id) { T objectFound = sessionStrategy.Invoke().Get<T>(id); return objectFound; }
}
Container .AddFacility<WcfFacility>() .AddFacility<AutoTxFacility>() .AddFacility<NHibernateFacility>() .Register(Component.For<INHibernateInstaller>().ImplementedBy<DatabaseMapInstaller>().LifeStyle.Singleton) .Register( Component.For<IServiceBehavior>().ImplementedBy<WcfSessionPerRequestBehavior>() .LifeStyle.PerWcfOperation()) .Register( Component.For<ICallContextInitializer>().ImplementedBy<WcfSessionPerRequestCallContextInitializer>() .LifeStyle.PerWcfOperation())
- Service classes are agnostic to all of these. They are registered to container with their interfaces and hosted by wcf integration facility.
Quick answer;
I think I see your point.
You want to have a session per wcf operation, and that is the point of your code.
I would ask you to consider what business/transaction boundary that you are trying to delineate – often if you are using a relational database you care about your consistency, what you are looking for is a transaction and because NHibernate is a ORM intimately bound to transactions, session-per-tx is what you are looking for. I think.
Having attributes doesn’t couple your code in any negative way. They can be removed without affecting the control flow.
It seems your changes are ok, but you will have to have a transasction there somewhere.
Cheers,
Henrik
From: Berke Sokhan [mailto:berke...@gmail.com]
Cc: Henrik Feldt
Sent: den 23 maj 2011 19:32
Hi Berke,
I can garantuee that ISessionManager is exactly the same instance all the time, because it’s a singleton.
The ISession it is a factory of, however, is not. It is per transaction per default – if you choose to use it differently, like you are to make it ‘cleaner’, you will have to handle the release of it. That is not handled for you – don’t just dispose it, also release it from the MicroKernel/Windsor in your dispose method.
By default the lifestyle requires an ambient transaction. If you want to handle transactions yourself, please set the ambient transaction (pseudo-code):
NHibernate.ITransaction tx = ses…Begin(…);
using (new TxScope( new CommittableTransaction().EnlistDurable(tx.SqlTransaction : IDbTransaction)))
{// perform call
} // tx scope is disposed and ambient tx reset.
This is among other things what the tx project does for you. (besides handling the error conditions which you code doesn’t – what happens if your tx deadlocks or livelocks? Your session won’t be disposed until GC/Finialization, right? A quick hack would be a try{}finally{} in your Dispose() method, to fix that problem.
In any case; you want to use SQL Server as your tx coordinator and not LTM, so: then you need to wire up services depending on ISessionManager differently, pseudo code again:
Component.Register<ICallContextInitializer>( … ).Depends(Key.Name(“<<your session manager key>>” + NHibernateFacility.SessionManagerSuffix))
.. same for IServiceBehavior.
And then your ISession will be resolved with a transient lifestyle.
PS: Also, if you have it like this, calls to other services won’t join your transaction unless they are in the same app domain (and hence same SqlConnection pool), so you won’t transaction flow out of the box, because you don’t set the ambient tx.
Cheers,
Henrik
PS Ah; in terms of transactions;
public ctor(ITransactionManager m)
{
this.m = m;
}
…
public object BeforeInvoke(…) { this.m.BeginTransaction(); }
This is a lot better than using
session.BeginTransaction
It simple handles more error cases for you.
Cheers,
Henrik
From: castle-pro...@googlegroups.com [mailto:castle-pro...@googlegroups.com] On Behalf Of Berke Sokhan
Sent: den 24 maj 2011 16:21
To: Henrik Feldt; castle-pro...@googlegroups.com; nhu...@googlegroups.com
Hi!
No problem, glad to help! It would be great if your experiences translate to a better experience using these facilities, by you sharing them with us.
Regards,
To: Henrik Feldt; castle-project-users@googlegroups.com; nhu...@googlegroups.com
2011/5/24 Henrik Feldt <hen...@haf.se>
2011/5/23 Henrik Feldt <hen...@haf.se>
To post to this group, send email to castle-project-users@googlegroups.com.
To unsubscribe from this group, send email to castle-project-users+unsub...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-project-users@googlegroups.com.
To unsubscribe from this group, send email to castle-project-users+unsub...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-project-users@googlegroups.com.
To unsubscribe from this group, send email to castle-project-users+unsub...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-project-users@googlegroups.com.
To unsubscribe from this group, send email to castle-project-users+unsub...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-project-users@googlegroups.com.
To unsubscribe from this group, send email to castle-project-users+unsub...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/castle-project-users?hl=en.
--
You received this message because you are subscribed to the Google Groups "Castle Project Users" group.
To post to this group, send email to castle-project-users@googlegroups.com.
To unsubscribe from this group, send email to castle-project-users+unsub...@googlegroups.com.