3

I know I can register a new "Service Endpoint" in MS CRM and use that to send messages to Azure Service Bus, but this... isn't really what I'm looking for. The above method ends up sending a serialized RemoteExecutionContext.

In my case, I want to have full control over what the Service Bus messages will contain. This means serializing my own classes.

I've tried using the WindowsAzure.ServiceBus nugget (and ILmerging the new DLL) but this only works in a non-sandboxed setting (on-premise CRM), but I'd also like for my solution to work in CRM Online. When attempting to use the same code in CRM Online then attempting to create a TopicClient throws an error:

System.Security.SecurityException: That assembly does not allow partially trusted callers

Is there any way around the above problem?

MBender
  • 5,395
  • 1
  • 42
  • 69
  • 1
    WindowsAzure.ServiceBus has open sources on GitHub. Is there any particular reason that you can't take the sources and just compile the together with your CRM plugins? – Pawel Gradecki Sep 03 '17 at 08:06
  • @PawelGradecki Hmm, I can try, but I somehow doubt this will solve the problem. This might be because the CRM Sandbox doesn't trust `System.Web` (even you can't even use `UrlEncode`, for example). BTW: The problem is NOT in merging external DLL's. I've successfully used plenty of external DLLs in my CRM solutions running in sandbox mode. – MBender Sep 04 '17 at 06:49
  • I know thread is old, I am stuck on same problem. As mentioned in answer easy way to use Rest api for service bus and send/consume messages but I wish to register Endpoint and NOT Plugin Step. Note only register Endoint and then in my plugin I could consume `iserviceendpointnotificationservice` **execute** method to send context of plugin. But instead of context I wish to send custom message. Any Idea this can be done? – AnkUser Apr 23 '20 at 09:05
  • @AnkUser This is a completely different question and is outside of the scope of what is discussed here. – MBender Apr 23 '20 at 11:37
  • I see. I am sending custom message from MS CRM plugin is what been discussed here, and I think I have almost similar req, infact solution which works is what I am leaning to but need to know more info about iserviceendpointnotificationservice which is derived from plugincontext – AnkUser Apr 23 '20 at 15:15

4 Answers4

2

Here is a way to do this without additional classes and without using ILMerge. I verified this works in CRM online version 9.1.0.646 (Dynamics 365 Customer Engagement)

Your plugin project will need the following references and using statements.

References: System.Net, System.Net.Http Usings: System.Net, System.Net.Http, System.Security.Cryptography, System.Globalization

In this sample code, I have built up a string variable named json that is the JSON message I want to post. Then the following code will send the message.

string asbUri = 
    "https://<azurenamespace>.servicebus.windows.net/<topicname>/messages";
TimeSpan ts = new TimeSpan(0, 0, 90);
string sasToken = GetSASToken("sb://<azurenamespace>.servicebus.windows.net", " 
    <nameofSASkey>", "<SASKeyValue>", ts);

HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", sasToken);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, asbUri)
{
    Content = new StringContent(json, Encoding.UTF8, "application/json")
};
HttpResponseMessage response = client.SendAsync(request).Result;

private static string GetExpiry(TimeSpan ttl)
{
   TimeSpan expirySinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1) + ttl;
   return Convert.ToString((int)expirySinceEpoch.TotalSeconds);
}

public static string GetSASToken(string resourceUri, string keyName, string key, 
TimeSpan ttl)
{
   var expiry = GetExpiry(ttl);
   //string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
   //NOTE: UrlEncode is not supported in CRM, use System.Uri.EscapeDataString instead
   string stringToSign = Uri.EscapeDataString(resourceUri).ToLowerInvariant() + "\n" 
        + expiry;
        HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));

   var signature = 
   Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
   var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature 
       sr={0}&sig={1}&se={2}&skn={3}",
       Uri.EscapeDataString(resourceUri).ToLowerInvariant(), 
       Uri.EscapeDataString(signature), expiry, keyName);
   return sasToken;
}
Andy Arndt
  • 21
  • 3
1

I found a way that's compatible with Sandboxed MS CRM.

The idea is to use the Azure REST endpoint to send messages to. It's fairly easy to authenticate and use... at least if you have a working example, which I was able to find here.

It's a fairly decent sample, albeit a bit messy. Still, it shows how to get the basics working, which is authentication and the actual calls.

(Minor note: reading topic messages from ASB based on the sample was not working reliably for me - it would work once and then would not work until the auth key timed out... this didn't bother me as I needed to send messages only, but if this is a functionality you need then this might be not as straightforward.)

MBender
  • 5,395
  • 1
  • 42
  • 69
1

I know this is an old question but I had to do something similar recently and I used the SharedVariable Collection to pass additional details and parameter to the ServiceBus.

This is an example:

context.SharedVariables.Add("AttachmentType", attachmentType);
David Noreña
  • 3,951
  • 1
  • 28
  • 43
Gary Cook
  • 19
  • 2
  • This doesn't solve the problem of how to actually send a custom data type as the message over to Service Bus, as it still utilizes the `RemoteExecutionContext` as the basis of the SB calls... – MBender Nov 17 '18 at 12:02
0

For CRM Online you could take the logic of message conversion/processing outside of sandbox. It will require to have some external compute. Considering you're already using CRM online, that shouldn't be an issue.

An approach you could take is to convert CRM constructed RemoteExecutionContext to whatever type you want. There's a sample of how to integrate Dynamics 365 with NServiceBus, which take this approach as well. The compute I was referring to would be the equivalent of the CRMAdapterEndpoint endpoint from the sample. The endpoint is using a Mapper object to convert JSON serialized RemoteExecutionContext to custom types, ContactCreate and ContactUpdate. That would allow you to achieve what you want.

Sean Feldman
  • 23,443
  • 7
  • 55
  • 80
  • While technically correct, I'm trying to simplify the solution, not make it more complex. :/ The need to have control over the messages stems from exactly this fact - I COULD use the `RemoteExecutionContext`, but sending a serialized model class seems far far easier to use later on. – MBender Sep 04 '17 at 06:48