Error in Sandbox Plugin

Question Status

Verified
Antony Ellis asked a question on 26 May 2016 11:02 AM

Hi Folks,

System.Runtime.Serialization.SerializationException: Microsoft Dynamics CRM has experienced an error. <ErrorCode>-2147220970</ErrorCode> with according to Microsoft translates to "An unexpected error occurred".

I am using the latest online version of CRM Dynamics 2016. Trying to create a Task following the creation of an Account by using early bound assembly which has been ILMerged and part of the one Plugin.dll. This all works fine without error in all of the following scenarios:

  • Running the Plugin On Premise (isolation mode: None)
  • Running the same code but substitute early binding to late binding in sandbox
  • Running almost identical code via an external console application
  • Removing the reference to Assembly (Common.dll) and instead include the CS files directly within the Plugin

The above projects are part of the same VS CRM solution and are referencing the same identical early bound library. In my case this is called CRM Common.DLL therefore at the moment I have no reason to believe there is something wrong with the plugin assembly itself; unless there are some other steps I should have taken specifically for an assembly which runs in Sandbox. When I debug the plugin, I find that is comes back with an error message "account id <guid xxxxxxxxx> Does Not Exist". This I do not understand as I have an Account GUID returned in code but neither the Account or subsequent task gets created.

I've tried various other permutations when setting the EntityReference for RegardingObjectId none of them work in sandbox, all of them work on premise ( e.g. RegardingObjectId=incomingAccount.ToEntityReference() or using the output parameter shown in code below). 

The plugin is registered:

  • Isolation: Sandbox
  • Pipeline: Post Operation
  • Synchronous
  • Create message
  • Primary Entity: Account

I have also tested this changing assembly isolation mode to "asynchronous" in that Scenario the Account gets created but the Task does not get created and the plugin trace shows it later errors with the same obscure Account Id does not exist.

My code

 public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService _tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            try
            {
                IPluginExecutionContext _context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

                IOrganizationServiceFactory _factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

                IOrganizationService _service = (IOrganizationService)_factory.CreateOrganizationService(_context.UserId);
              
                Entity incoming = (Entity)_context.InputParameters["Target"];
                Guid _accountId =  new Guid(_context.OutputParameters["id"].ToString());
               
                _tracer.Trace("Incoming entity identified as "  + incoming.LogicalName);
        

                Task followup = new Task();
                followup.Subject = "Testing EARLY binding task creation via DLL in simple plugin";
                followup.RegardingObjectId = new EntityReference("account", _accountId);

                _tracer.Trace("Attempting to create Task and providing the account GUID " + _accountId.ToString());

              _service.Create(followup);

            }
            catch (Exception ex)
            {

                _tracer.Trace("Error in Create Task : " + ex.Message);

            }
     }

Though I have workarounds

It seems to me there are clear gaps in my knowledge when it comes to referencing early bound types from within a plugin assembly and so I would like to understand how to do this where you have a merged DLL running in Sandbox. Often, I have many projects in a CRM solution, so having them all referencing the one common library is advantageous.

Thanks for help

Reply
Antony Ellis responded on 26 May 2016 11:34 AM

Ok got this to work now! Using the follow approach can anyone explain in clear English what is happening? I suspect it has something to do with serialization between types however would appreciate a clearer understanding. Surely I don't have to do this everytime I wish to reference early bound library within a plugin?

// followup is type Task

 Entity passThis = followup.ToEntity<Entity>();

// previously  _service.Create(followup);

_service.Create(passThis);

Reply
Scott Durow (MVP) responded on 26 May 2016 1:27 PM

So removing the reference to Common.dll allows you to run the plugin ok?

I avoid using ILMerge to add in common code because of these kind of problems - I simply reference the early bound type classes using 'Link' in visual studio rather than doing an ILMerge.

Reply
Antony Ellis responded on 26 May 2016 1:42 PM

Thanks Scott! there are some occasions I suspect where unfortunately we have no choice but to merge (e.g.. using a 3rd party assembly like JSON.Net) though perhaps there is a better tool out there to use? I've simplified the code a little now and this works with both Online and On Premise though still seems strange to me than on premise had no issue converting Task to Entity but online wouldn't have it.

localContext.OrganizationService.Create(followup.ToEntity<Entity>());

Reply
Scott Durow (MVP) responded on 26 May 2016 3:36 PM

I know what you mean - but I usually try to find an alternative  - JSON.NET is open source and so you can actually compile the source code into into your Plugins.  

Reply
Suggested Answer
Scott Durow (MVP) responded on 26 May 2016 3:42 PM

The way the OrganizationService Proxy binds to early bound types is that it does some pretty complex stuff in searching the current app domain & executing assembly for matching classes decorated with the correct Attributes. I suspect that in the process of doing the ILMerge with your Common.dll - this process was not working correctly and so it caused an error that manifested it's self as something different.

Reply
Antony Ellis responded on 27 May 2016 3:29 AM

Thanks so are you saying that I shouldn't have to do .ToEntity<Entity>() conversions before passing on an object to the CRUD methods when using a custom EB assembly? i.e. That this is not the standard approach and is believed to be a consequence of something going wrong with the ILMerge? I have tried this with two different ILMerge solutions and Common libraries. The other one was built by a 3rd party and the same error manifested leading me to believe it was something more fundamental going wrong rather than ILMerge. I was thinking perhaps it was more to do with internal security and being unable to do a cast in Sandbox, that's my only theory for why *it works On Premise* but not Online... It would be good to know how other developers are handling this scenario and if the said error manifests in CRM 2016 online? or on premise Sandbox isolation mode.

Reply
Verified Answer
Scott Durow (MVP) responded on 27 May 2016 4:01 AM

Hi,

I always use early bound types to perform OrganizationService operations in CRM Online sandboxed plugins - so I think the issue is with the binding to the Common.dll - you shouldn't need to use ToEntity<Entity>()

It could be something to do with the ILMerge strong name signing not being valid for sandboxed code. I recommend compiling the early bound types into your plugin since this will work in all situations and is fully supported.

Hope this helps

Reply
Antony Ellis responded on 27 May 2016 4:36 AM

Thanks Scott.. think you are right if I can avoid the issue in the first place I should really do that instead. ILMerge is a Microsoft Research thing so should be supported. However, now that I have experienced these issues seems to me the low risk approach is to do as you suggest.

Reply
Verified Answer
Antony Ellis responded on 27 May 2016 7:53 AM

So I have lost two days researching this online, an issue that has been around since Dynamics CRM 4.0. If you provide a SDK then of course I will want to use Early binding and have that code work both on premise and in the cloud. It shouldn't require ILMerge, SvcUtil, XRMToolBox, RibbonWorkbench and all the other tools. The developer toolkit should be provided by Microsoft with every CRM/SDK/VS release and give us the means to extend the CRM Application all in a supported way.

ILMerge Unsupported

Finally got this working without having to do the Create(followup.ToEntity<Entity>()) when referencing the common early bound library which to be fair, was a workable solution though adding to the code. Nowhere online could I find where it says for your DLL go to Properties, then edit the AssemblyInfo.cs file add this line:

[assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssemblyAttribute()]

Thanks to Daryl L Bar which gave me at least somewhere to start looking. Now ILMerge will work as expected in Sandbox where you have this scenario (albeit not officially supported by Microsoft) and from what I have read online is likely to fall over in more complex coding scenarios with the merge processing getting confused over types and not linking stuff correctly. This being something which Scott Durow has alluded to on my question. The reason of course my class wasn't working was that ILMerge did not have the Proxy Types from SDK leading to a Serialization error when this was being executed in Sandbox.

Microsoft CRM team reply:

August 23rd, 2015:

Hi @Eric W. Cahoon – thanks for your comments.  We've updated the blog post to reflect that ILMerge is not supported, and have clarified any language that previously indicated that it was supported.  As for the larger question, it is on our radar but at this point has not been prioritized.

blogs.msdn.microsoft.com/.../how-to-reference-assemblies-from-plug-ins

The larger question is:

"How do I safely reference a custom early bound library DLL from a Plugin" that will work with both Cloud and On Premise. Or more specifically how to use an early bound library that needs to execute in Sandbox Isolation mode without having to include the file in each and every project?

The general consensus would seem to be:

For Online and this Scenario:

  • Avoid ILMerge and instead look for open source alternatives and include the code in your Plugin and Custom workflow activities
  • Add the early bound code class files to each and every CRM project and Solution
  • Use Add-Link from within Visual Studio (you reference the external file via a Link) to have it compiled within your plugin/workflow

Of course for on Premise you have more options:

  • Register the assembly on disk with any referenced assemblies in the same directory.
  • Register the referenced assembly in the GAC

For now I am going to keep using ILMerge purely for the Early Bound class library until I no doubt come across a future problem at which point hopefully will remember this "Microsoft 10 Year old problem" which is yet to be a priority. I hope this thread helps save people time, pain and frustration in the future when they are faced with this seemingly simple scenario.

If you want a step by step guide on how to get this working check out my Dynamics 365 blog two-part series: 

https://antonyellisdynamicscrm.wordpress.com/2016/10/12/how-to-register-an-early-bound-library-within-microsoft-dynamics-crm-online-sandbox-isolation-mode-part-1/

https://antonyellisdynamicscrm.wordpress.com/2016/10/21/how-to-register-an-early-bound-library-within-microsoft-dynamics-crm-online-sandbox-isolation-mode-part-2/

Reply
Scott Durow (MVP) responded on 27 May 2016 10:53 AM

Hi Antony, Thanks for the comprehensive write-up. I'm sure this will help folks in the future.

Reply
Verified Answer
Scott Durow (MVP) responded on 27 May 2016 4:01 AM

Hi,

I always use early bound types to perform OrganizationService operations in CRM Online sandboxed plugins - so I think the issue is with the binding to the Common.dll - you shouldn't need to use ToEntity<Entity>()

It could be something to do with the ILMerge strong name signing not being valid for sandboxed code. I recommend compiling the early bound types into your plugin since this will work in all situations and is fully supported.

Hope this helps

Reply
Verified Answer
Antony Ellis responded on 27 May 2016 7:53 AM

So I have lost two days researching this online, an issue that has been around since Dynamics CRM 4.0. If you provide a SDK then of course I will want to use Early binding and have that code work both on premise and in the cloud. It shouldn't require ILMerge, SvcUtil, XRMToolBox, RibbonWorkbench and all the other tools. The developer toolkit should be provided by Microsoft with every CRM/SDK/VS release and give us the means to extend the CRM Application all in a supported way.

ILMerge Unsupported

Finally got this working without having to do the Create(followup.ToEntity<Entity>()) when referencing the common early bound library which to be fair, was a workable solution though adding to the code. Nowhere online could I find where it says for your DLL go to Properties, then edit the AssemblyInfo.cs file add this line:

[assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssemblyAttribute()]

Thanks to Daryl L Bar which gave me at least somewhere to start looking. Now ILMerge will work as expected in Sandbox where you have this scenario (albeit not officially supported by Microsoft) and from what I have read online is likely to fall over in more complex coding scenarios with the merge processing getting confused over types and not linking stuff correctly. This being something which Scott Durow has alluded to on my question. The reason of course my class wasn't working was that ILMerge did not have the Proxy Types from SDK leading to a Serialization error when this was being executed in Sandbox.

Microsoft CRM team reply:

August 23rd, 2015:

Hi @Eric W. Cahoon – thanks for your comments.  We've updated the blog post to reflect that ILMerge is not supported, and have clarified any language that previously indicated that it was supported.  As for the larger question, it is on our radar but at this point has not been prioritized.

blogs.msdn.microsoft.com/.../how-to-reference-assemblies-from-plug-ins

The larger question is:

"How do I safely reference a custom early bound library DLL from a Plugin" that will work with both Cloud and On Premise. Or more specifically how to use an early bound library that needs to execute in Sandbox Isolation mode without having to include the file in each and every project?

The general consensus would seem to be:

For Online and this Scenario:

  • Avoid ILMerge and instead look for open source alternatives and include the code in your Plugin and Custom workflow activities
  • Add the early bound code class files to each and every CRM project and Solution
  • Use Add-Link from within Visual Studio (you reference the external file via a Link) to have it compiled within your plugin/workflow

Of course for on Premise you have more options:

  • Register the assembly on disk with any referenced assemblies in the same directory.
  • Register the referenced assembly in the GAC

For now I am going to keep using ILMerge purely for the Early Bound class library until I no doubt come across a future problem at which point hopefully will remember this "Microsoft 10 Year old problem" which is yet to be a priority. I hope this thread helps save people time, pain and frustration in the future when they are faced with this seemingly simple scenario.

If you want a step by step guide on how to get this working check out my Dynamics 365 blog two-part series: 

https://antonyellisdynamicscrm.wordpress.com/2016/10/12/how-to-register-an-early-bound-library-within-microsoft-dynamics-crm-online-sandbox-isolation-mode-part-1/

https://antonyellisdynamicscrm.wordpress.com/2016/10/21/how-to-register-an-early-bound-library-within-microsoft-dynamics-crm-online-sandbox-isolation-mode-part-2/

Reply
Suggested Answer
Scott Durow (MVP) responded on 26 May 2016 3:42 PM

The way the OrganizationService Proxy binds to early bound types is that it does some pretty complex stuff in searching the current app domain & executing assembly for matching classes decorated with the correct Attributes. I suspect that in the process of doing the ILMerge with your Common.dll - this process was not working correctly and so it caused an error that manifested it's self as something different.

Reply