I have a WCF client that I'm having problems with.
From time to time I am getting this exception: Cannot access a disposed object
. This is how I am opening the connection:
private static LeverateCrmServiceClient crm = null;
public static CrmServiceClient Get(string crmCertificateName)
{
if (crm != null)
{
crm.Close();
}
try
{
crm = new LeverateCrmServiceClient("CrmServiceEndpoint");
crm.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
crmCertificateName);
}
catch (Exception e)
{
log.Error("Cannot access CRM ", e);
throw;
}
return crm;
}
As you can see, I am closing and reopening the connection each time. What do you think might be the problem?
Stack:
System.ServiceModel.Security.MessageSecurityException: Message security verification failed. ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.ServiceModel.Security.SymmetricSecurityProtocol'.
at System.ServiceModel.Channels.CommunicationObject.ThrowIfClosedOrNotOpen()
at System.ServiceModel.Security.MessageSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
--- End of inner exception stack trace ---
Server stack trace:
at System.ServiceModel.Security.MessageSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at Externals.CrmService.ICrmService.GetTradingPlatformAccountDetails(Guid ownerUserId, String organizationName, String businessUnitName, Guid tradingPlatformAccountId)
at MyAppName.Models.ActionsMetadata.Trader.BuildTrader(Guid tradingPlatformAccountId) in C:\Users\X\Documents\Visual Studio 2010\Projects\MyAppName\MyAppName\Models\ActionsMetadata\Trader.cs:line 120
at MyAppName.Models.ActionsMetadata.Trader.Login(String accountNumber, String password) in C:\Users\X\Documents\Visual Studio 2010\Projects\MyAppName\MyAppName\Models\ActionsMetadata\Trader.cs:line 48
at MyAppName.Models.ActionsMetadata.Handlers.LoginHandler.Handle(StepHandlerWrapper wrapper) in C:\Users\X\Documents\Visual Studio 2010\Projects\MyAppName\MyAppName\Models\ActionsMetadata\Handlers\LoginHandler.cs:line 23
at MyAppName.Models.ActionsMetadata.Handlers.HandlerInvoker.Invoke(IAction brokerAction, ActionStep actionStep, Dictionary`2 stepValues, HttpContext httpContext, BaseStepDataModel model) in C:\Users\X\Documents\Visual Studio 2010\Projects\MyAppName\MyAppName\Models\ActionsMetadata\Handlers\StepServerInoker.cs:line 42
at MyAppName.Controllers.LoginController.Login(String step) in C:\Users\X\Documents\Visual Studio 2010\Projects\MyAppName\MyAppName\Controllers\LoginController.cs:line 35
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
From that stack trace, I assume you have an ASP.NET MVC application with some code that calls that Get
method to get a CrmServiceClient
object, and then proceeds to call various methods on that CrmServiceClient
object. For example, part of the login process does this.
The way your Get
method works is, each time it is called, it will first close the CrmServiceClient
object that it returned previously (regardless of whether or not it is still being used) then create and return a new one.
Imagine two users attempt to log in to your application at almost the same time - within milliseconds of each other. The thread handling the first user's login calls Get
and gets its CrmServiceClient
object, then a millisecond later the thread handling the second user's login calls Get
, which causes the first thread's CrmServiceClient
object to be closed. However, the first thread is still running, and now when it attempts to call a method on it's CrmServiceClient
object, it gets a System.ObjectDisposedException: Cannot access a disposed object
.
"As you can see, I am closing and reopening the connection each time."
The code currently in your Get
method is not a good way to achive this. You should instead make it the caller's responsibility to close (or dispose) the CrmServiceClient
object, and you should perhaps rename your Get
method to Open
or Create
to suggest this. Callers should use a using
statement to ensure that the object is closed/disposed, regardless of any exceptions that occur:
using (CrmServiceClient client = CrmServiceFactory.Get("my-crm-certificate"))
{
client.Something();
client.GetTradingPlatformAccountDetails();
client.SomethingElse();
} // client is automatically closed at the end of the 'using' block