Writing A CRM Callout Assembly: A How To

I was recently asked to write a callout assembly to be attached to a client's CRM workflow service.  A callout assembly (in this regard) is an assembly that is attached to the CRM workflow service and is sort of like an external event handler.  So when someone adds/updates/deletes records in CRM, the callout assembly is fired.  I had never done it before, but I thought it wouldn't be too much of a problem.  There are some samples in the CRMSDK and I was sure that searching on Google and MSDN would yield the answers to any questions I'd have.  I was wrong.

There isn't too much help in either of those two sources, as I found out the hard way.  This possibly isn't the most common thing in the world to do, I'm guessing.

The problem I had with the "readme"s in the callout samples given in the CRMSDK is that they just blindly say "do this" and "do that," without any explanation of what's going on.  They also missed a step, as far as I can see.  They don't explain the callout.config.xml file at all.  I'll attempt to explain things, step-by-step, since I don't really see a definitive explanation of how these callout assemblies work anywhere (I've seen similar posts on other blog sites with less information, however).

Step 1: create a blank solution with a class library project inside.

Step 2: add a web reference to your CRM web service.  It should look something like this: http://<yourserver>/mscrmservices/2006/CrmService.asmx, as the readme got correct.  If you need to login, you better have all that information handy, as you'll need it later.  This information will include: a) user name b) password c) domain d) CRM server.

Step 3: add a reference to the Microsoft.Crm.Platform.Callout.Base.dll file.  If you need to download that, you should do so.  I had to.  This is necessary for the next step.

Step 4: create a class for your callout that extends the CrmCalloutBase class.  You will need the dll from the previous step to do this.  Make sure this class has "using Microsoft.Crm.Callout;" in the using directives for the base class and "using <yourassemblyname>.<yourwebreferencename>;" for the web service.  If you are ever going to need to connect to the web server (say, if a child object is updated/added/deleted, the parent needs to update its information, for example), then you'll need the user name, password, domain and CRM server information from before.  You'll need this code to access the CRM web service:

CrmService service = new CrmService();
service.Url = <CRM_SERVER> + "crmservice.asmx";
service.PreAuthenticate = true;
service.Credentials = new NetworkCredential(<CRM_USER>, <CRM_PASSWORD>, <CRM_DOMAIN>);
WhoAmIRequest userRequest = new WhoAmIRequest();
WhoAmIResponse userResponse = (WhoAmIResponse)service.Execute(userRequest);

And your service is now ready to use.  I.e. if a child's information is edited, you may want to use the web service to update the parent.  This may be the whole reason for creating a callout assembly.

Step 5: decide what events you want to listen into.  Do you want to react to whenever a case is submitted?  Do you want to react to whenever someone updates their contact information?  Do you want to check submitted information before it gets submitted?  Whatever it is, there are plenty of things to listen into.  To find out what you can listen to, inside your class, start a new line (with intellisense enabled) and type override and then a space to see what possibilities you have.  For quick reference, there's pre- and post-:

  • Create
  • Update
  • Delete
  • Assign
  • Set state
  • MergePersonally
  • PreSend
  • PostDeliver

Step 6: implement the code you want to fire when your event is triggered.  You can override a bunch of methods (all of them, actually) in the same callout class, so feel free.  I overrode three in mine.  I don't quite understand why the entity context is passed, as you can set your event to only listen for when an event is fired for an entity of a particular type.  I personally don't have any code in my callout I just wrote that worries about what type of entity I'm dealing with.

Step 7: create the callout.config.xml file.  Assuming you're using CRM 3.0, you should have this line of code as your second line of code instead of the ones in the CRMSDK samples: "<callout.config version="3.0" xmlns=" http://schemas.microsoft.com/crm/2006/callout/">" or your config file might not be recognized.  Inside the callout config file, you'll need a <callout> node for each event/entity combination you are listening into that looks like this: <callout entity="myentityname" event="Post/Pre<see above bulleted list for ideas>"> with an optional onerror property with the value of "abort" or "ignore."  Inside this node, you'll need a subscription node that looks something like this: <subscription assembly="myassembly.dll" class="myfullnamespace.myclass(from step four)">.  Inside this node, you'll need one or more <postvalue> nodes IF you are listening in on a "Post" event (not a "Pre" event).  Each of these <postvalue> nodes needs to have the name of a property of the entity you are going to work with in your code.  For example, if you working with an incident entity and want to look at the ticket number in your callout assembly's class, you'll need this inside your subscription node: <postvalue>ticketnumber</postvalue>.  One short cut, if you're going to use a lot of properties, or an expanding number of properties (in case your assembly will be modified as time goes on) or if you're just really lazy is to put this in your subscription: <postvalue>@all</postvalue>, and you'll then get all of the properties.  That's what I did.  Mainly because I knew that the objects don't have a lot of properties.  I would also suggest writing to a log file throughout your code so you can debug more easily.

Step 8: compile your code and attach it.  Attaching it is pretty tricky, so make sure you pay close attention.  I am not sure you'll need to do all of these things, but I did, so here's what should work for sure.  First, go into the services console (of the CRM server...) and stop the Microsoft CRM Workflow Service.  Next, if you've already deployed a previous version of your callout assembly, RENAME IT.  It won't let you delete it, but you can rename it...  Then, move (the new version of) your callout assembly to the \Program Files\Microsoft CRM\Server\bin\assembly folder.  Then, restart IIS.  I'll wait...  Ok, now start the Microsoft CRM Workflow Service.  Delete the old assembly (the one you renamed) if this isn't the first time you deployed it.  IF YOU MAKE ANY CHANGES TO THE DLL OR THE CALLOUT.CONFIG.XML FILE(S), YOU MUST REPEAT STEP 8 OR IT WON'T TAKE EFFECT!

That should do it!  Now, if you have trouble getting into the workflow or think it's too restrictive/not powerful enough, you can write C# code (or VB code, if you are so inclined) that can do whatever you want and then just attach it to the workflow process using the process outlined above.  Happy calling out!

Published Monday, October 23, 2006 2:19 PM by vbullinger

Comments

# re: Writing A CRM Callout Assembly: A How To

One thing that seemed to give me problems when moving this to production (this was written after it was in a development environment) was the credentials.  I mentioned it was important to know the user name, password and domain.  Meh, not so much.  An easy way to do this without knowing the accurate credentials is as follows:

CrmService service = new CrmService();
service.Url = "http://<yourserver>/mscrmservices/2006/CrmService.asmx";
service.PreAuthenticate = true;
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
WhoAmIRequest userRequest = new WhoAmIRequest();
WhoAmIResponse userResponse = (WhoAmIResponse)service.Execute(userRequest);

In this scenario, you are using the "default credentials" which are the credentials of the user that fired off the callout.  This is sometimes a good idea and sometimes a bad one, depending on your scenario.  In mine, it was a great solution because it would mean that the only way I could modify the objects I wanted to modify in my callout is if the user that instigated the action that fired off my callout has the power to modify the objects my callout wants to modify.  This is more than likely a good solution, but use it with caution.

Thursday, November 02, 2006 2:05 PM by vbullinger

# re: Writing A CRM Callout Assembly: A How To

Hi.

I faced some problems using webservices in a callout.

While running an import script which calls the execute command several times, the callout is executed, but when no more connections available, the following exception is thrown in IIS:

The underlying connection was closed: An unexpected error occurred on a send

Remarkable is that, this exceptions does not appear to be thrown back to the import script.

I'm still looking for a possible workaround on this one..

Thursday, August 09, 2007 1:10 PM by Dido

# re: Writing A CRM Callout Assembly: A How To

Hi. It seems i've found a solition for my own comment

Add this to your reference.cs:

protected override System.Net.WebRequest GetWebRequest(Uri uri)

       {

           //throw new Exception("Custom WebRequest override code hit!!");

           System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);

           webRequest.ConnectionGroupName = "CrmCalloutA";

           return webRequest;

       }

       [System.Web.Services.Protocols.SoapDocumentMethodAttribute("", RequestNamespace = "urn:objective.com", ResponseNamespace = "urn:objective.com", Use = System.Web.Services.Description.SoapBindingUse.Encoded, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare)]

       [return: System.Xml.Serialization.SoapElementAttribute("result")]

       public object send(Object request)

       {

           System.Net.ServicePoint sp;

           object[] results;

           sp = System.Net.ServicePointManager.FindServicePoint(new Uri(this.Url));

           //DON'T USE THESE!

           //sp.ConnectionLeaseTimeout = 0

           //sp.MaxIdleTime = 0

           try

           {

               results = this.Invoke("send", new object[] { request });

           }

           catch (Exception ex)

           {

               if (ex.Message.Equals("The underlying connection was closed: A connection that was expected to be kept alive was closed by the server."))

               {

                   //log the fact that the server has unexpectedly dropped the connection

                   //<insert log code here (couldn't be bothered writing this bit)>

                   //close the client connection (using the ConnectionGroupName set above)

                   sp.CloseConnectionGroup("CrmCalloutA");

                   //reinvoke

                   results = this.Invoke("send", new object[] { request });

               }

               else

               {

                   throw (ex);

               }

           }

           return (object)results[0];

       }

Thursday, August 09, 2007 2:08 PM by Dido

# re: Writing A CRM Callout Assembly: A How To

Hi,

I found error "declaration Expected" for staements

   service.Uri = "any url"

service.Uri = "ASD"

   service.Credentials = new NetworkCredential(<CRM_USER>, <CRM_PASSWORD>, <CRM_DOMAIN>)

   dim userResponse as WhoAmIResponse = (WhoAmIResponse)service.Execute(userRequest)

and when i write 'service' then press dot(.) nothing is shown but when i press ctrl+space then properties are shown.

one thing more; there is no "url" property for service object else it is uri?

Please help me now.

Its urgent.

Tuesday, September 04, 2007 5:07 AM by Ulfat

# re: Writing A CRM Callout Assembly: A How To

Well, Ulfat, step one?  Don't use VB.

Step two?  That error message is giving no help at all: http://ryangregg.com/PermaLink,guid,6aa52998-8f10-4726-897d-08eb0823126d.aspx

Notice how this is only a problem in VB?

Url is, indeed, a property of the CrmService object.  I've used it many, many times.  I also can't help you with the Intellisense "problem."  I have noticed that happen to me, occasionally, and either rebuilding the solution/project or closing it/opening it fixes the issue.  I question if you are using the correct object (CrmService).

Tuesday, September 04, 2007 8:50 AM by vbullinger

# re: Writing A CRM Callout Assembly: A How To

Hi,

Do I have to use VS 2003 to create callouts

Regards

Wednesday, October 31, 2007 9:35 AM by mgad

# re: Writing A CRM Callout Assembly: A How To

To mgad;

Do you "have" to use VS 2003 to create callouts?  I've heard no, you don't, but I've never gotten one to work in VS 2005 (or later).  CRM was written in VS 2003 with ASP.Net 1.1, so it (for some reason) seems to be easier to write callouts in VS 2003.  I would strongly suggest it, since several people I know have tried to write them in VS 2005 with absolutely zero success, even though they very confidently said they'd get it to work.  I know you should always use the latest technologies when doing things, but still, don't waste your time.

Wednesday, October 31, 2007 10:13 AM by vbullinger

# re: Writing A CRM Callout Assembly: A How To

thank you, vince. It was of great help!

By the way, for those, who are lazy enough, I'd recommend:

http://blogs.msdn.com/arash/archive/2006/08/25/use-visual-studio-2005-to-build-crm-callouts.aspx

I use it being happy to have found it once!

Thursday, November 01, 2007 11:23 AM by Vadim

# re: Writing A CRM Callout Assembly: A How To

To Vadim;

I've had that post sent to many on so many occasions, it's not even funny.  And not one of those people have gotten it to work.  So, while it may be possible, please a) don't send me another link to it, I found it on my own when I first wrote a callout and didn't get it to work and b) try it first.  If you get it to work, post what you figured out and what we all might be missing.  IN THAT VERY POST, YOU HAVE THIS PARAGRAPH:

"However, building Callouts for CRM V3.0 is officially supported only on VS 2003.  The explanation is rather simple.  CRM platform itself is built on .NET Framework V1.1 and VS 2003.  The platform runs as part of the IIS process and Callouts are loaded in that process when web services are called.  Common Language Runtime (CLR) does not support loading two versions of .NET code into the same process (from my old days at VS team there are a lot of reasons for that including complexity of versioning, isolation and upgrade, etc).  So if you build and compile your Callout using VS 2005, the callout cannot be loaded by our platform and you get an error."

Please!  Don't waste your time.  You now have the reasoning behind not wasting your time, so please don't do it.  And I really don't want to hear about that blog post again :)

Thursday, November 01, 2007 11:33 AM by vbullinger

# re: Writing A CRM Callout Assembly: A How To

To mgad,

You don't have to use VS 2003 but it is recommended.  I have successfully deployed callouts using VS 2005.  However, you have to deploy them using the .NET 1.1 (1.0) Framework.  VS 2003 uses the 1.1 Framework natively but VS 2005 uses the 2.0 Framework.  You can use a product called MSbee to build VS2005 projects in .NET 1.1 but it's a pain in the @ss.

Matt

Thursday, November 29, 2007 1:49 PM by Matt Skelton

# re: Writing A CRM Callout Assembly: A How To

Hi

I have an entity that when a field is updated my callout performs a calculation and updates another field to a new value.

This all works ok,  however the performance is really bad.

Im getting the following error in the event log

postupdate, exception: Microsoft.Crm.Callout.CrmCalloutException: invocation timed out.

  at Microsoft.Crm.Callout.CalloutHost.PostUpdate(CalloutUserContext userContext, CalloutEntityContext entityContext)

Any ideas

Cheers

John

Wednesday, March 05, 2008 8:57 AM by John Leatherbarrow

# re: Writing A CRM Callout Assembly: A How To

To John Leatherbarrow:

You sure you don't want to try to do this in JScript?

After that, I'd have to see your code to be able to debug this.  It gets inside of your code and then hangs, so... it's hard to tell you your problem.  It doesn't have anything to do with the callout or the connection.

Wednesday, March 05, 2008 12:44 PM by vbullinger

# re: Writing A CRM Callout Assembly: A How To

Hi,good site!

Wednesday, April 23, 2008 9:32 AM by availsoto

Leave a Comment

(required) 
(required) 
(optional)
(required)