摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复267或者20171129可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me 。

我这里使用的CRM是8.2.1.176的本地部署版本。

假设一种场景,需要在创建联系人(contact)的时候将其负责人(ownerid)更改为某个团队,最容易想到的当然是用Pre阶段的插件,我使用了如下的插件代码:

  1. using Microsoft.Xrm.Sdk;
  2. using System;
  3. using System.ServiceModel;
  4.  
  5. namespace CrmVSSolution.Plugins
  6. {
  7. public class PreContactCreate : IPlugin
  8. {
  9. public void Execute(IServiceProvider serviceProvider)
  10. {
  11. ITracingService tracingService =
  12. (ITracingService)serviceProvider.GetService(typeof(ITracingService));
  13. tracingService.Trace(string.Format("{0} trigged on {1}.", "CrmVSSolution.Plugins.PreContactCreate", DateTime.UtcNow.ToString()));
  14.  
  15. IPluginExecutionContext context = (IPluginExecutionContext)
  16. serviceProvider.GetService(typeof(IPluginExecutionContext));
  17.  
  18. if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
  19. {
  20. Entity currentEntity = (Entity)context.InputParameters["Target"];
  21. tracingService.Trace(string.Format("Current entity logial entity is {0} {1}.", currentEntity.LogicalName, currentEntity.Id));
  22. if (currentEntity.LogicalName != "contact")
  23. return;
  24. IOrganizationServiceFactory serviceFactory =
  25. (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
  26. IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
  27.  
  28. try
  29. {
  30. currentEntity["ownerid"] = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F"));
  31. tracingService.Trace(string.Format("{0} successfully executed on {1}.", "CrmVSSolution.Plugins.PreContactCreate", DateTime.UtcNow.ToString()));
  32. }
  33. catch (FaultException<OrganizationServiceFault> ex)
  34. {
  35. throw new InvalidPluginExecutionException("An error occurred in CrmVSSolution.Plugins.PreContactCreate.", ex);
  36. }
  37. catch (Exception ex)
  38. {
  39. tracingService.Trace("An non-faultException occurred in CrmVSSolution.Plugins.PreContactCreate {0}.", ex.ToString());
  40. throw;
  41. }
  42. }
  43. }
  44. }
  45. }

然后我去界面创建联系人,发现会报错,报错的主要信息是: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"));)更改为

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

去测试,代码虽然没有报错了,但是创建后,看到的负责人还是创建者,囧,没有用。

放在Pre Operation阶段不行,我放在Post Operation阶段呢,于是我又注册了一个PostContactCreate的插件,代码如下:

  1. using Microsoft.Xrm.Sdk;
  2. using System;
  3. using System.ServiceModel;
  4.  
  5. namespace CrmVSSolution.Plugins
  6. {
  7. public class PostContactCreate : IPlugin
  8. {
  9. public void Execute(IServiceProvider serviceProvider)
  10. {
  11. ITracingService tracingService =
  12. (ITracingService)serviceProvider.GetService(typeof(ITracingService));
  13. tracingService.Trace(string.Format("{0} trigged on {1}.", "CrmVSSolution.Plugins.PostContactCreate", DateTime.UtcNow.ToString()));
  14.  
  15. IPluginExecutionContext context = (IPluginExecutionContext)
  16. serviceProvider.GetService(typeof(IPluginExecutionContext));
  17. IOrganizationServiceFactory serviceFactory =
  18. (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
  19. IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
  20.  
  21. if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
  22. {
  23. Entity currentEntity = (Entity)context.InputParameters["Target"];
  24. tracingService.Trace(string.Format("Current entity logial entity is {0} {1}.", currentEntity.LogicalName, currentEntity.Id));
  25. if (currentEntity.LogicalName != "contact")
  26. return;
  27. try
  28. {
  29. currentEntity["ownerid"] = new EntityReference("team", Guid.Parse("E4CC382D-02B9-E511-80DC-000D3A804C3F"));
  30. service.Update(currentEntity);
  31. tracingService.Trace(string.Format("{0} successfully executed on {1}.", "CrmVSSolution.Plugins.PostContactCreate", DateTime.UtcNow.ToString()));
  32. }
  33. catch (FaultException<OrganizationServiceFault> ex)
  34. {
  35. throw new InvalidPluginExecutionException("An error occurred in CrmVSSolution.Plugins.PostContactCreate.", ex);
  36. }
  37. catch (Exception ex)
  38. {
  39. tracingService.Trace("An non-faultException occurred in CrmVSSolution.Plugins.PostContactCreate {0}.", ex.ToString());
  40. throw;
  41. }
  42. }
  43. }
  44. }
  45. }

然后我去测试,创建一个联系人的时候同时填写邮箱会出现如下报错:

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

  1. 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:
  2. <OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  3. <ActivityId>9dd983d8-3f09-4bb8-9570-92528fb7071e</ActivityId>
  4. <ErrorCode>-2147204784</ErrorCode>
  5. <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  6. <Message>Generic SQL error.</Message>
  7. <Timestamp>2017-11-29T13:52:13.8847228Z</Timestamp>
  8. <ExceptionRetriable>false</ExceptionRetriable>
  9. <ExceptionSource i:nil="true" />
  10. <InnerFault i:nil="true" />
  11. <OriginalException i:nil="true" />
  12. <TraceText i:nil="true" />
  13. </OrganizationServiceFault>

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

>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约束,我看了这个索引,定义如下:

  1. CREATE UNIQUE NONCLUSTERED INDEX [ndx_for_forward_update] ON [dbo].[EmailSearchBase]
  2. (
  3. [ParentObjectId] ASC,
  4. [EmailColumnNumber] ASC
  5. )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)

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

再做个简单的实验就会知道,如果插入记录的时候不包括邮箱(emailaddress1),则不会报错,而且记录创建后的负责人也是指定的负责团队。

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

方法一是将这个插件做成异步执行的插件,弊端就是异步执行的插件与当前主操作不在同一个事务中,有可能失败。

这个方法就是创建完毕后还看不到效果,要等到异步插件执行完毕,在插件跟踪日志中(如果启用了)是可以看到运行记录的:

方法二还是保持插件是同步的,不过将插件中的如下代码用Assign来代替:

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

更改成:

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

这个就是在事务中了,和主操作同成功或者同失败。但是SDK中推荐的写法一般是用Update消息代替其他的消息,比如上面用到的AssignRequest消息。

Dynamics 365创建电子邮箱字段包含值的联系人同时更改负责人的方法。的更多相关文章

  1. Dynamics 365中使用计算字段自动编号字段实时工作流自动生成分组编码加流水号的自动编号字段值

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  2. Dynamics 365创建用户提示:您正在尝试使用已由其他用户使用的域登录来创建用户。如何解决。

    摘要: 本人微信公众号:微软动态CRM专家罗勇 ,回复287或者20181128可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me ...

  3. 在dynamics 365 中,看字段的描述需要到系统字段设置里面才能看到,这里提供一种sql直接看字段和实体名描述的方法

    1.在crm对应的主数据库执行下面存储过程: -- ============================================= -- Author: <Author,,Name& ...

  4. 创建Car类,包含name,price属性,构造器等方法,创建测试类,在main方法中创建Set接口的实现类,添加5个以上的Car对象,遍历集合元素,验证重复元素是否过滤了; 如果没有过滤,实现过滤功能;把每个小车的price降10000元,再遍历,查看price是否已改变

    i汽车类 package com.lanxi.demo2_3; public class Car { private String name; private int price; @Override ...

  5. Dynamics 365新功能:可编辑的网格(行内编辑)

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复238或者20161127可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  6. Dynamics 365中审核用户权限变化的一种方法

    摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复268或者20180311可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...

  7. Dynamics 365中使用Web API将查找字段的值设置为空值的方法。

    摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复270或者20180424可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...

  8. Dynamics 365客户端编程示例:两个选项集字段的联动

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  9. Dynamics 365利用HTML页面创建实体记录并同步上传附件

    我是微软Dynamcis 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

随机推荐

  1. win10 uwp 截图 获取屏幕显示界面保存图片

    本文主要讲如何保存我们的屏幕显示的,保存为图片,也就是截图,截我们应用显示的. UWP有一个功能,可以截图,RenderTargetBitmap 我们首先写一个Grid,我们需要给他名字,我这里给他S ...

  2. Android基础知识笔记01—框架结构与四大组件

    -----------Andriod 01--------------->>> Andriod系统架构    linux内核与驱动层. 系统运行库层. 应用框架层. 应用层 内核驱动 ...

  3. vue搭建项目前奏曲——vue-cli

    vue-cli是快速构建这个单页应用的脚手架,这个可是官方的.官方给的建议,如果你是初次尝试Vue,哪就老老实实用普通的书写引入js文件,这里牵扯太多的东西,例如webpack.Node.js.npm ...

  4. 在HTM中显示播放视频

    注意:video中source 源文件地址src替换成你的video路径<html>    <button onclick="playPause();">播 ...

  5. Oracle学习笔记之存储过程

                                                                                                        ...

  6. javaWeb 基础知识

    cookie  深度解析:  http://blog.csdn.net/ghsau/article/details/20395681 cookie 的作用: 利用存在客户端本地的缓存让无状态的服务器也 ...

  7. 4天精通arcgis

    真是掉进了一个史无前例的坑 --ArcGIS产品线为用户提供一个可伸缩的,全面的GIS平台. 这是百科的介绍,简单来讲,这就是一个地图,可以搞事情. 学的是ArcGIS API for JavaScr ...

  8. 微信公众号开发(三)获取access_token

    微信公众号开发(三)获取access_token 1.说明 access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存.acce ...

  9. java语言在某个数组中查找某个字符出现的次数

    package com.llh.demo; import java.util.Scanner; /** * * @author llh * */ public class Test { /* * 在某 ...

  10. Game of Connections

    Game of Connections Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...