I need to create a service which will maintain a WCF session. In the constructor I read in data from the DB and when the session ends I have to save it back.
If I understand correctly the session ends when I call Close() on the Client (My client ServiceClient was created with SvcUtil.exe).
When I test it I see that it is sometimes called after approx. 10 minutes, sometimes after 20 minutes and sometimes not at all.
So when is the destructor called?
Service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service:IService
{
private User m_User = null;
public Service()
{
m_User = User.LoadFromDB();
}
~Service()
{
m_User.SaveToDB();
}
public void SetName(string p_Name)
{
m_User.Name = p_Name;
}
}
Web.config
<?x开发者_运维知识库ml version="1.0"?>
<configuration>
<system.web>
<sessionState timeout="2" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service name="Karatasi.Services.B2C" behaviorConfiguration="ServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:19401/B2C.svc"/>
</baseAddresses>
</host>
<endpoint
address=""
binding="wsHttpBinding"
bindingConfiguration="test"
contract="Karatasi.Services.IB2C"
/>
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"
/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="test" receiveTimeout="00:01:00" >
<reliableSession enabled="true" ordered="false" inactivityTimeout="00:01:00"/>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Client
ServiceClient serviceClient = null;
try
{
serviceClient = new ServiceClient();
serviceClient.SetName("NewName");
Console.WriteLine("Name set");
}
catch (Exception p_Exc)
{
Console.WriteLine(p_Exc.Message);
}
finally
{
if (serviceClient != null)
{
if (serviceClient.State == CommunicationState.Faulted)
{
serviceClient.Abort();
}
else
{
serviceClient.Close();
}
}
Console.ReadKey();
}
From docs
The programmer has no control over when the destructor is called because this is determined by the garbage collector. The garbage collector checks for objects that are no longer being used by the application. If it considers an object eligible for destruction, it calls the destructor (if any) and reclaims the memory used to store the object. Destructors are also called when the program exits.
There is a problem with your implementation. To persist data you are using destructor. This is wrong because destructors cannot be called deterministically, they are processed in a separate finalization queue. This means that even though you have destroyed the object, its destructor may not be immediately called.
How to fix this
Remove the destructor and use IDisposable pattern instead, put save logic into Dispose. Once the session is terminated, WCF will call IDisposable.Dispose
public class Service:IService, IDisposable
{
public void Dispose()
{
//your save logic here
}
}
EDIT
Pls also see the comment to this answer. I actually agree that IDisposable
isn't the proper place for database commits, didn't occur to me before. Additionally to the solutions provided in the comment you can use explicit session demarcation
精彩评论