从 MS CRM 插件(沙盒)向 Azure 服务总线发送自定义消息

Send custom messages to Azure Service Bus from MS CRM Plugins (Sandbox)

我知道我可以在 MS CRM 中注册一个新的 "Service Endpoint" 并使用它向 Azure 服务总线发送消息,但这...并不是我真正想要的。上面的方法最终发送了一个序列化的 RemoteExecutionContext.

就我而言,我想完全控制服务总线消息将包含的内容。这意味着序列化我自己的 类.

我已经尝试使用 WindowsAzure.ServiceBus nugget(和 ILmerging 新 DLL),但这只适用于非沙盒设置(内部部署 CRM),但我也想要我的解决方案在 CRM Online 中工作。尝试在 CRM Online 中使用相同的代码然后尝试创建 TopicClient 时会引发错误:

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

有没有办法解决上述问题?

对于 CRM Online,您可以将消息逻辑 conversion/processing 置于沙盒之外。它将需要一些外部计算。考虑到您已经在线使用 CRM,这应该不是问题。

您可以采用的一种方法是将 RemoteExecutionContext 构建的 CRM 转换为您想要的任何类型。有一个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。那会让你实现你想要的。

我找到了一种与 Sandboxed MS CRM 兼容的方法。

想法是使用 Azure REST 端点向其发送消息。验证和使用相当容易......至少如果你有一个工作示例,我能够找到它 here.

这是一个相当不错的样本,尽管有点乱。尽管如此,它还是展示了如何让基础知识发挥作用,即身份验证和实际调用。

(小注:根据示例从 ASB 读取主题消息对我来说工作不可靠 - 它会工作一次然后直到授权密钥超时才工作......这并没有打扰我,因为我只需要发送消息,但如果这是您需要的功能,那么这可能不是那么简单。)

我知道这是一个老问题,但我最近不得不做类似的事情,我使用 SharedVariable Collection 将额外的细节和参数传递给 ServiceBus。

这是一个例子:

context.SharedVariables.Add("AttachmentType", attachmentType);

这是一种无需额外 类 且无需使用 ILMerge 即可执行此操作的方法。我在 CRM 在线版本 9.1.0.646 (Dynamics 365 Customer Engagement)

中验证了这一点

您的插件项目将需要以下引用和使用语句。

参考文献:System.Net、System.Net.Http Usings: System.Net, System.Net.Http, System.Security.Cryptography, System.Globalization

在此示例代码中,我建立了一个名为 json 的字符串变量,它是我想要 post 的 JSON 消息。然后下面的代码将发送消息。

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;
}