PDA

View Full Version : NHibernate session per WCF operation.


chrc2
08-05-2008, 04:45 PM
Hi,
I might be missing the point completely, but, in InstanceContextMode.PerCall a new instance of the service is created for each operation, and it is disposed immediately afterwards. Therefore, given a factory that can provide nhibernate sessions, a reference to the current session could be held in the service itself, and the session could be disposed of in the dispose method.
Wouldn’t this work?
Or why are there so many posts and blogs about people using ICallContextInitializer and the like to hold sessions?

example code:

namespace Spring.WcfQuickStart
{
[ServiceBehavior(
ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.PerCall,
IncludeExceptionDetailInFaults = true)]
public class CalculatorService : HasAdaptors, ICalculator, IDisposable
{
private ISession thisSession;
private ISession Session
{
get
{
if (thisSession == null)
{
thisSession = SessionFactory.CreateSession();
}
return thisSession;
}
}
private int sleepInSeconds;

public int SleepInSeconds
{
get { return sleepInSeconds; }
set { sleepInSeconds = value; }
}

[Required]
public ISessionFactory SessionFactory
{
get { return sessionProvider; }
set { sessionProvider = value; }
}

private ISessionFactory sessionProvider;

public double Add(double n1, double n2)
{
using (TransactionScope tx = new TransactionScope())
{
Console.WriteLine("--- Start : Add ---");

OperationContext.Current.OperationCompleted += Current_OperationCompleted;
Transaction.Current.TransactionCompleted += Current_TransactionCompleted;

Folder folder = new Folder();
folder.Name = "dada";
folder.SecurityClassification = SecurityClassification.Secret;

Session.CreateOrUpdate(folder);

Thread.Sleep(sleepInSeconds*1000);
tx.Complete();
}
foreach (IAdaptor adaptor in Adaptors)
{
adaptor.Notify();
}
Console.WriteLine("--- End : Add ---");

double result = n1 + n2;

//CalculatorSubscription.NotifyClientsSomeThingHappe ned(result);
new LayerNotifier().SomeThingHappened(result);

return result;
}

#region IDisposable Members

public void Dispose()
{
Session.Close();
thisSession.Dispose();
thisSession = null;
Console.WriteLine("calc service shutting down");
}

#endregion
}
}

Bruno Baia
08-05-2008, 05:39 PM
Hi,

Did you configured your service as a prototype in Spring's configuration ? (singleton="false").


- Bruno

FYI: We are currently working on WCF integration for 1.2 release.

chrc2
08-05-2008, 10:02 PM
Hi, and thanks for the quick reply!

No, its (almost) the standard definition from the example, below are the relevant parts from the app.config. Would it make a difference if i added singleton="false"?

It seams to be working fine, but i havent tested with lots of concurrent clients acessing the service - so it might just be my small single user example that works... any thoughts on the soundness of this would be most welcome.

As i have hinted in the code,
(OperationContext.Current.OperationCompleted += Current_OperationCompleted;
Transaction.Current.TransactionCompleted += Current_TransactionCompleted;)
another approach would to use one of the above events to close the nhibernate session. I guess Transaction.Current.TransactionCompleted would be the best, giving the fastest termination of the session.

<object id="calculator"
type="Spring.WcfQuickStart.CalculatorService, Spring.WcfQuickStart.ServerApp">
<property name="SleepInSeconds" value="1"/>
<property name="SessionFactory" ref="NHibernateSessionFactory"/>
<property name="InternalAdaptors">
<list>
<ref object="Adaptor2"/>
<ref object="Adaptor1"/>
</list>
</property>
</object>

<object type="Spring.ServiceModel.ServiceExporter, Spring.Services">
<property name="TargetName" value="calculator" />
</object>

chrc2
08-06-2008, 03:37 PM
Made some experiments today, and it seems as though the service instance is being used by other threads, even though e.g. "Programming WCF Services" states otherwise. I tried making the service singleton=false in the spring D.I., but that didnt help either. Finally i made the session (and the test counter i was testing with) threadstatic (se code below). This helped, and i closed the session on the Transaction.Current.TransactionCompleted event. I might work this into my framework in a nice way.

[ThreadStatic]
private static int threadId = 0;

[ThreadStatic]
private static ISession thisSession;
private ISession Session
{
get
{
if (thisSession == null)
{
thisSession = SessionFactory.CreateSession();
}
if (threadId != Thread.CurrentThread.ManagedThreadId && threadId != 0)
{
throw new ApplicationException("Other thread accessing this instance! " + threadId + " : " + Thread.CurrentThread.ManagedThreadId);
}
threadId = Thread.CurrentThread.ManagedThreadId;
return thisSession;
}
}

<object id="calculator"
type="Spring.WcfQuickStart.CalculatorService, Spring.WcfQuickStart.ServerApp"
singleton="false">
<property name="SleepInSeconds" value="1"/>
<property name="SessionFactory" ref="NHibernateSessionFactory"/>
<property name="InternalAdaptors">
<list>
<ref object="Adaptor1"/>
<ref object="Adaptor2"/>
</list>
</property>
</object>

Oh and setting singleton="false" in this line:

<object type="Spring.ServiceModel.ServiceExporter, Spring.Services" singleton="false">
<property name="TargetName" value="calculator" />
</object>

makes the client unable to find the service?

System.ServiceModel.EndpointNotFoundException: Could not connect to http##edited (url)##local
host:8000/Spring.WcfQuickStart/service. TCP error code 10061: No connection coul
d be made because the target machine actively refused it 127.0.0.1:8000. ---> S
ystem.Net.WebException: Unable to connect to the remote server ---> System.Net.S
ockets.SocketException: No connection could be made because the target machine a
ctively refused it 127.0.0.1:8000
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddre
ss socketAddress)
at System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP)
at System.Net.ServicePoint.ConnectSocketInternal(Bool ean connectFailure, Sock
et s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state,
IAsyncResult asyncResult, Int32 timeout, Exception& exception)
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.GetRequestStream()
at System.ServiceModel.Channels.HttpOutput.WebRequest HttpOutput.GetOutputStre
am()
--- End of inner exception stack trace ---

Server stack trace:
at System.ServiceModel.Channels.HttpOutput.WebRequest HttpOutput.GetOutputStre
am()
at System.ServiceModel.Channels.HttpOutput.Send(TimeS pan timeout)
at System.ServiceModel.Channels.HttpChannelFactory.Ht tpRequestChannel.HttpCha
nnelRequest.SendRequest(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Reques t(Message message, TimeS
pan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinde r.Request(Message messag
e, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(S tring action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan tim
eout)
at System.ServiceModel.Channels.ServiceChannel.Call(S tring action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.I nvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.I nvoke(IMessage message)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleRe turnMessage(IMessage req
Msg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateI nvoke(MessageData& msgDa
ta, Int32 type)
at Spring.WcfQuickStart.ICalculator.GetName()
at Spring.WcfQuickStart.ClientApp.Program.Main() in C:\Documents and Settings
\csz\My Documents\Spring.NET-20080729-2051\examples\Spring\Spring.WcfQuickStart\
src\Spring.WcfQuickStart.ClientApp\Program.cs:line 86