3

We have a situation where the CrmServiceClient class cannot be instantiated, with an 'Object reference not set to an object' error coming from deep within the bowels of the constructor. I've also received a Collection was modified; enumeration operation may not execute error in a few situations.

This does not happen all the time, but we seem to be able to reproduce it when we trigger multiple requests very quickly.

We create the object as follows:

 var ctx = new CrmServiceClient(ConfigurationManager.ConnectionStrings["Xrm"].ConnectionString);

The connection string is valid and we have set RequireNewInstance to true

We were originally using the ctx in a using block but we were advised that we shouldn't be disposing of the CrmServiceClient, so we've removed the using block, but this has not resolved the problem.

The stack trace is below - i've only pasted the relevant part. The stack leading up to this point can be any piece of code that attempts to connect to the CRM to retrieve data

   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at Microsoft.Xrm.Tooling.Connector.Utilities.GetOrgnameAndOnlineRegionFromServiceUri(Uri serviceUri, String& onlineRegion, String& organizationName, Boolean& isOnPrem)
   at Microsoft.Xrm.Tooling.Connector.CrmConnection.SetOrgnameAndOnlineRegion(Uri serviceUri)
   at Microsoft.Xrm.Tooling.Connector.CrmConnection..ctor(String serviceUri, String userName, String password, String domain, String homeRealmUri, String authType, String requireNewInstance, String clientId, String redirectUri, String tokenCacheStorePath, String loginPrompt, String certStoreName, String certThumbprint, String skipDiscovery)
   at Microsoft.Xrm.Tooling.Connector.CrmConnection..ctor(IDictionary`2 connection)
   at Microsoft.Xrm.Tooling.Connector.CrmConnection.Parse(String connectionString)
   at Microsoft.Xrm.Tooling.Connector.CrmServiceClient.ConnectToCrmWebService(String crmConnectionString)
   at Microsoft.Xrm.Tooling.Connector.CrmServiceClient..ctor(String crmConnectionString)
link64
  • 1,944
  • 1
  • 26
  • 41
  • What version of SDK are you using ? – Ondrej Svejdar Jun 18 '19 at 08:31
  • Also looking at code most like reason for this one is the uri is somehow strange. Can you share the uri ? – Ondrej Svejdar Jun 18 '19 at 08:38
  • @OndrejSvejdar, as mentioned, the connection string is fine as this works almost 90% of the time. I believe i've tracked down the issue but I am still confirming, most likely post an update tomorrow - thanks for helping Im using the latest package for xrm tooling from Nuget, 9.0.2.16 I believe – link64 Jun 18 '19 at 09:56

1 Answers1

4

I believe I've tracked down the issue. I used DotNetPeek to look at the underlying code that was failing. The static method GetOrgnameAndOnlineRegionFromServiceUriwas where the error was occurring.

I tracked it down to a static list (discoSvcs) that was being set to null before the method returns. Other threads that call this method are also trying to do things with this list. It ends up that there is a race condition where one thread could check to see if it isn't null.

The only way I can get around it now is making sure only one CrmServiceClient is being instantiated at any time, by using a lock. This isn't ideal but I am running out of time

Static list definition

namespace Microsoft.Xrm.Tooling.Connector
{
  public class Utilities
  {
    private static CrmOnlineDiscoveryServers discoSvcs;
    private static List<string> _autoRetryRetrieveEntityList;

    private Utilities()
    {
    }

Problem Function

The static list variable is checked at the beginning of this function and if it is null, it is populated with some values. It is then used later in the method before being set to null in the finally block.

  public static void GetOrgnameAndOnlineRegionFromServiceUri(
      Uri serviceUri,
      out string onlineRegion,
      out string organizationName,
      out bool isOnPrem)
    {
      isOnPrem = false;
      onlineRegion = string.Empty;
      organizationName = string.Empty;
      if (serviceUri.Host.ToUpperInvariant().Contains("DYNAMICS.COM") || serviceUri.Host.ToUpperInvariant().Contains("MICROSOFTDYNAMICS.DE") || (serviceUri.Host.ToUpperInvariant().Contains("MICROSOFTDYNAMICS.US") || serviceUri.Host.ToUpperInvariant().Contains("DYNAMICS-INT.COM")))
      {
        if (Utilities.discoSvcs == null)
          Utilities.discoSvcs = new CrmOnlineDiscoveryServers();
        try
        {
          List<string> stringList = new List<string>((IEnumerable<string>) serviceUri.Host.Split(new string[1]
          {
            "."
          }, StringSplitOptions.RemoveEmptyEntries));
          organizationName = stringList[0];
          stringList.RemoveAt(0);
          StringBuilder stringBuilder = new StringBuilder();
          foreach (string str in stringList)
          {
            if (!str.Equals("api"))
              stringBuilder.AppendFormat("{0}.", (object) str);
          }
          string crmKey = stringBuilder.ToString().TrimEnd('.').TrimEnd('/');
          stringBuilder.Clear();
          if (!string.IsNullOrEmpty(crmKey))
          {
            CrmOnlineDiscoveryServer onlineDiscoveryServer = Utilities.discoSvcs.OSDPServers.Where<CrmOnlineDiscoveryServer>((Func<CrmOnlineDiscoveryServer, bool>) (w =>
            {
              if (w.DiscoveryServer != (Uri) null)
                return w.DiscoveryServer.Host.Contains(crmKey);
              return false;
            })).FirstOrDefault<CrmOnlineDiscoveryServer>();
            if (onlineDiscoveryServer != null && !string.IsNullOrEmpty(onlineDiscoveryServer.ShortName))
              onlineRegion = onlineDiscoveryServer.ShortName;
          }
          isOnPrem = false;
        }
        finally
        {
          Utilities.discoSvcs.Dispose();
          Utilities.discoSvcs = (CrmOnlineDiscoveryServers) null;
        }
      }
      else
      {
        isOnPrem = true;
        if (((IEnumerable<string>) serviceUri.Segments).Count<string>() < 2)
          return;
        organizationName = serviceUri.Segments[1].TrimEnd('/');
      }
    }
link64
  • 1,944
  • 1
  • 26
  • 41