运用Fiddler修改请求信息透过Web API执行Dynamics 365操作(Action)实例。Dynamics 365创造电子邮箱字段包含值的联络员同时更改负责人的措施。

本人微信以及易信公众号: 微软动态CRM专家罗勇
,回复261或者20170724可利得本文,同时可以在第一中取得我颁发的风行的博文信息,follow
me!我之网站是 www.luoyong.me 。

摘要: 本人微信跟易信公众号: 微软动态CRM专家罗勇
,回复267要20171129只是惠及得本文,同时可于率先内取得我公布的新型的博文信息,follow
me!我之网站是 www.luoyong.me 。

Web API于Dynamics CRM中之法力更加强大,Dynamics 365 (Dynamics CRM
V8.2) 又增了有些透过通过Web API可以履的操作(Action) ,比如
ExecuteWorkflow这个Action,具体Web
API可以实行怎样操作为?可以参照SDK的 Web API Action Reference
章节,也发生在线版本,在线版本是 Web API Action
Reference
。当然,这个节的情节会变动,要查阅Dynamics
CRM对应版本的SDK。今天而教的凡运Fiddler的Replay功能通过Web
API调用操作。

我这边用的CRM是8.2.1.176之地头部署版本。

以继承读之前,如果您对经Web
API执行操作没有定义,强烈建议你先读书我之文章:Dynamics CRM
2015/2016初特点的二十四:使用Web
API执行操作 。

假要同一种植现象,需要以创建联系人(contact)的下用其主管(ownerid)更改为有团体,最容易想到的自然是故Pre阶段的插件,我动用了之类的插件代码:

咱俩事先押 ExecuteWorkflow
Action
的证实,可以掌握其是一个绑定操作, 它来一个输入参数 EntityId
,也发出返参数,是只asyncoperation
实体记录类型。为了测试需要,先使寻找一个工作流,我这里来一个工作流如下,我这边故意没选中
作为比如需流程
,是坐我怀念看不选中这个是不是也得以由此代码执行这个工作流,当然在界面上通过运行工作流此时凡是寻觅不顶之工作流的。

using Microsoft.Xrm.Sdk;
using System;
using System.ServiceModel;

namespace CrmVSSolution.Plugins
{
    public class PreContactCreate : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService =
                (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            tracingService.Trace(string.Format("{0} trigged on {1}.", "CrmVSSolution.Plugins.PreContactCreate", DateTime.UtcNow.ToString()));

            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                Entity currentEntity = (Entity)context.InputParameters["Target"];
                tracingService.Trace(string.Format("Current entity logial entity is {0} {1}.", currentEntity.LogicalName, currentEntity.Id));
                if (currentEntity.LogicalName != "contact")
                    return;
                IOrganizationServiceFactory serviceFactory =
                    (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

                try
                {
                    currentEntity["ownerid"] = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F"));
                    tracingService.Trace(string.Format("{0} successfully executed on {1}.", "CrmVSSolution.Plugins.PreContactCreate", DateTime.UtcNow.ToString()));
                }
                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException("An error occurred in CrmVSSolution.Plugins.PreContactCreate.", ex);
                }
                catch (Exception ex)
                {
                    tracingService.Trace("An non-faultException occurred in CrmVSSolution.Plugins.PreContactCreate {0}.", ex.ToString());
                    throw;
                }
            }
        }
    }
}

图片 1

接下来我去界面创建联系人,发现会报错,报错的根本信息是:Changing security
attributes is not allowed in stage 20 plugins.
这个错误信息翻译过来就是于插件等也20(也尽管是Pre
Operation阶段)的插件代码中莫允更改安全性能。假设我用内部的基本点代码(currentEntity[“ownerid”]
= new EntityReference(“team”,
Guid.Parse(“E4CC382D-02B9-E511-80DC-000D3A804C3F”));)更改为 

 

currentEntity["owningteam"] = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F"));
currentEntity.Attributes.Remove("owninguser");

下一场自己需要摸索一漫长是工作流对应实体的平漫漫记下,我此选择相同修记下。顺便介绍下哪些获取记录ID的计,右击一久记下,选择
在新窗口被开辟 。

失测试,代码虽然尚未报错了,但是创建后,看到底主任要创建者,囧,没有因此。

图片 2

置身Pre Operation阶段很,我在Post
Operation阶段也,于是我还要注册了一个PostContactCreate的插件,代码如下:

 

using Microsoft.Xrm.Sdk;
using System;
using System.ServiceModel;

namespace CrmVSSolution.Plugins
{
    public class PostContactCreate : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService =
                (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            tracingService.Trace(string.Format("{0} trigged on {1}.", "CrmVSSolution.Plugins.PostContactCreate", DateTime.UtcNow.ToString()));

            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory =
                    (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                Entity currentEntity = (Entity)context.InputParameters["Target"];
                tracingService.Trace(string.Format("Current entity logial entity is {0} {1}.", currentEntity.LogicalName, currentEntity.Id));
                if (currentEntity.LogicalName != "contact")
                    return;
                try
                {
                    currentEntity["ownerid"] = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F"));
                    service.Update(currentEntity);
                    tracingService.Trace(string.Format("{0} successfully executed on {1}.", "CrmVSSolution.Plugins.PostContactCreate", DateTime.UtcNow.ToString()));
                }
                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException("An error occurred in CrmVSSolution.Plugins.PostContactCreate.", ex);
                }
                catch (Exception ex)
                {
                    tracingService.Trace("An non-faultException occurred in CrmVSSolution.Plugins.PostContactCreate {0}.", ex.ToString());
                    throw;
                }
            }
        }
    }
}

下一场就是见面当新窗口中开拓,可以看出打开记录下的URL是近似: https://demo.luoyong.me/main.aspx?etc=10007&extraqs=&histKey=789763582&id=%7bB907DE1B-CF99-E611-8161-000D3A80C8B8%7d&newWindow=true&pagetype=entityrecord&sitemappath=SFA%7cExtensions%7cly\_test\#348434379
。这个URL中的id=%7b 和 %7d之间的虽是是记录的ID。

 

咱尚需执行的工作流的ID,在解决方案中双击打开该工作流,有像样这样的URL:https://demo.luoyong.me/sfa/workflow/edit.aspx?\_CreateFromId=%7b2721DA92-65A4-E511-80CB-000D3A80CE7F%7d&\_CreateFromType=7100&appSolutionId=%7b2721DA92-65A4-E511-80CB-000D3A80CE7F%7d&id=%7b6BEBC426-F722-4B64-AE5D-0DA379F8A8C4%7d 

下一场自己失去测试,创建一个挂钩人之上以填写邮箱会出现如下报错:

相同的我们可拿到之工作流的ID。

图片 3

本身此借助Fiddler来Replay,启用Fiddler抓包,右击一个央,选择 Replay
> Reissue from Composer.

点击下载日志文件,下载的日志如下:

 图片 4

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Generic SQL error.Detail: 
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  <ActivityId>9dd983d8-3f09-4bb8-9570-92528fb7071e</ActivityId>
  <ErrorCode>-2147204784</ErrorCode>
  <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  <Message>Generic SQL error.</Message>
  <Timestamp>2017-11-29T13:52:13.8847228Z</Timestamp>
  <ExceptionRetriable>false</ExceptionRetriable>
  <ExceptionSource i:nil="true" />
  <InnerFault i:nil="true" />
  <OriginalException i:nil="true" />
  <TraceText i:nil="true" />
</OrganizationServiceFault>

在Fiddler的Composer中开拓页面类似如下:

这种Generic SQL
error按照经验来讲一般是插件报错,我去服务器上看报错如下:

图片 5

>Crm Exception: Message: Generic SQL error., ErrorCode: -2147204784,
InnerException: System.Data.SqlClient.SqlException (0x80131904):
不可知以备唯一索引“ndx_for_forward_update”的靶子“dbo.EmailSearchBase”中插入重复键的履。重复键值为
(17b08b7c-0cd5-e711-834e-000d3a80c8b8, 42)。
话语已告一段落。
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception,
Boolean breakConnection, Action`1 wrapCloseInAction)
at
System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject
stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior,
SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet
bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader
ds, RunBehavior runBehavior, String resetOptionsString, Boolean
isInternal, Boolean forDescribeParameterEncryption)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean
async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry,
SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String
method, TaskCompletionSource`1 completion, Int32 timeout, Task& task,
Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at
System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1
completion, String methodName, Boolean sendToPipe, Int32 timeout,
Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at
Microsoft.Crm.CrmDbConnection.InternalExecuteWithRetry[TResult](Func`1
ExecuteMethod, IDbCommand command)
at Microsoft.Crm.CrmDbConnection.InternalExecuteNonQuery(IDbCommand
command, Boolean capturePerfTrace)
at
Microsoft.Crm.BusinessEntities.BusinessProcessObject.ExecuteNonQuery(IDbCommand
command, ISqlExecutionContext context)
at Microsoft.Crm.BusinessEntities.EmailAddressTrigger.DoCreate(Guid
parentObjectId, EmailInfo createInfo)
at Microsoft.Crm.BusinessEntities.EmailAddressTrigger.Create(Guid id)
at
Microsoft.Crm.BusinessEntities.TriggersExtension.ExecuteTriggers(BusinessEntity
entity, ArrayList triggers, OperationType operationType)
at
Microsoft.Crm.BusinessEntities.BusinessProcessObject.PostCreateEventHandler.Invoke(Object
sender, ExtensionEventArgs e)
at
Microsoft.Crm.BusinessEntities.BusinessProcessObject.Create(IBusinessEntity
entity, ExecutionContext context, Boolean createWithPipeline)
at
Microsoft.Crm.Common.ObjectModel.ContactService.Create(IBusinessEntity
entity, ExecutionContext context)

 

囧,这个报错让丁莫名其妙啊,EmailSearch实体的确在(系统规范实体),但是我从未明了的动是实体,这个提示是背了唯一索引ndx_for_forward_update约束,我看了是目录,定义如下:

接下来我们即便可以转移请求方法,请求URL,请求头Header和请求体Body。我这里转如下:

CREATE UNIQUE NONCLUSTERED INDEX [ndx_for_forward_update] ON [dbo].[EmailSearchBase]
(
    [ParentObjectId] ASC,
    [EmailColumnNumber] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80)

POST的URL我改变成为了:https://demo.luoyong.me/api/data/v8.2/workflows(6BEBC426-F722-4B64-AE5D-0DA379F8A8C4)/Microsoft.Dynamics.CRM.ExecuteWorkflow

而此里面都未曾id为17b08b7c-0cd5-e711-834e-000d3a80c8b8这个记录啊,根据技术支持的说,这是因插入失败,记录回滚了,所以看不到。

 Request
Body我转化了之类,特别注意这个Cookie的元素值如果封存,用来说明的,当然为会见晚点:

双重开只大概的试就会了解,如果插入记录的时候不包邮箱(emailaddress1),则无见面报错,而且记录创建后底领导人员也是指定的负责组织。

Content-Type: application/json; charset=utf-8
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Cookie: ReqClientId=dff59da0-52dd-42f9-8ab9-e62ad9b24e57;
e9cd027f-26a3-e511-80c6-000d3a807ec7_bd2a5c49-6b08-4eda-8a15-84159d9fd349=/Date(1478082706255)/;
persistentNavTourCookie=HideNavTour;
CRM_MSG_BAR_ServiceDeskAlert%23e9cd027f-26a3-e511-80c6-000d3a807ec7=HideMessage;
MSISAuth=77u/PD94bWwgdmVyc2lvbj0iMSlSNWN6UEdTQm9Md0FnQUFFWU8ydHhxQXNxTVo0NS83WUJRL3pGdk1ZWndQWld6RnhjcVhHYWx6ZjZscFVsUVBnVWprSnArVVZYY0IxcGNsMXF5VmZ0UVm5nVnFSc3pxV1RKbjlrMWxlRWhB;
MSISAuth1=M3VIZmdZZmVF0eUNvbnRleHRUb2tlbj4=;
CRM_MSG_BAR_e9cd027f-26a3-e511-80c6-000d3a807ec7GetAppsForCrm=HideMessage;
CRM_MSG_BAR_EnableS2SAlert%23e9cd027f-26a3-e511-80c6-000d3a807ec7=HideMessage

图片 6

 Request Body我转化了:

 

{“EntityId”:”B907DE1B-CF99-E611-8161-000D3A80C8B8″}

 看来是坐插入入记录的时段传递了电子邮件导致,我个人认为这里是Dynamics
365哪儿做的问题。问题由问题,还得解决嘛。我这边提供零星种解决办法。

 我尽之说话会回来HTTP Status为500,内部错误:

道同样凡是将这插件做成异步执行之插件,弊端就是异步执行之插件与眼前主操作无在与一个事情中,有或破产。

HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal
Expires: -1
Server: Microsoft-IIS/8.5
REQ_ID: 5b92fe5f-7856-4817-bc18-210b20fa2b08
OData-Version: 4.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 24 Jul 2017 15:33:39 GMT
Content-Length: 2869

图片 7

{
“error”:{
“code”:””,”message”:”Workflow must be marked as on-demand or child
workflow.”,”innererror”:{
“message”:”Workflow must be marked as on-demand or child
workflow.”,”type”:”System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault,
Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35]]”,”stacktrace”:” at
Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.Execute(OrganizationRequest
request, CorrelationToken correlationToken, CallerOriginToken
callerOriginToken, WebServiceType serviceType, Boolean checkAdminMode,
ExecutionContext executionContext)\r\n at Microsoft.Crm.”
}
}
}

 

提醒很清楚,工作流必须要选中 作为比如需要流程
或者是子流程才行。于是自己住用该工作流,然后激活该工作流,再来尝试,执行结果如下:

本条方式就是是创建了后尚看不到效果,要当及异步插件执行了,在插件跟踪日志被(如果启用了)是足以看来运行记录之:

图片 8

图片 9

若果查原版的归内容是这般的:

 

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal
Expires: -1
Server: Microsoft-IIS/8.5
REQ_ID: 0ad59cc7-ab4e-49bf-a05b-8b522db29c75
Preference-Applied: return=representation
OData-Version: 4.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 24 Jul 2017 15:39:04 GMT
Content-Length: 38136

 方法二还是维持插件是共同的,不过以插件中的如下代码用Assign来代表:

{
“@odata.context”:”https://demo.luoyong.me/api/data/v8.2/$metadata\#asyncoperations/$entity","statecode":0,"asyncoperationid":"f9349b37-8670-e711-826c-000d3a80c8b8","\_regardingobjectid\_value":"b907de1b-cf99-e611-8161-000d3a80c8b8","\_owningextensionid\_value":"c8556024-8670-e711-826c-000d3a80c8b8","createdon":"2017-07-24T15:39:03Z","\_workflowactivationid\_value":"c8556024-8670-e711-826c-000d3a80c8b8","depth":1,"messagename":"ExecuteWorkflow","\_ownerid\_value":"e9cd027f-26a3-e511-80c6-000d3a807ec7","name":"\\u7f57\\u52c7\\u6d4b\\u8bd5\\u5b9e\\u4f53\\u5b57\\u6bb5\\u503c\\u53d8\\u66f4\\u540e\\u8fd0\\u884c\\u7684\\u5de5\\u4f5c\\u6d41","requestid":null,"friendlymessage":null,"retrycount":null,"startedon":null
}

currentEntity["ownerid"] = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F"));
service.Update(currentEntity);

 可以看出实行成功,我去界面上为堪看来实行了工作流:

更改成:

图片 10

AssignRequest assignReq = new AssignRequest()
{
   Assignee = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F")),
   Target = new EntityReference(context.PrimaryEntityName, context.PrimaryEntityId)
};
service.Execute(assignReq);

 

以此就是当业务中了,和主操作以及成功或者和失败。但是SDK中推荐的写法一般是故Update消息代替其他的音信,比如上面用到之AssignRequest消息。

 

相关文章