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


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

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

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

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

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

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

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

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

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

    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>

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

    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)

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

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

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

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

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

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

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

    更改成:

    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消息。

  • 相关阅读:
    安装RVM时报错 curl: (7) Failed to connect to raw.githubusercontent.com port 443: Connection refused
    如何查看docker run启动参数命令
    ProxySQL 基础篇
    Kafka too many open files解决
    离线方式搭建本地的nginx yum源
    Ceph配置参数详解
    osd自杀问题跟踪
    由一次slow-request浅谈Ceph scrub原理
    Power Apps 定制提交表单后的效果
    基于arm的Linux内核编译
  • 原文地址:https://www.cnblogs.com/luoyong0201/p/Dynamics_365_Create_Contact_Email_Owner_Generic_SQL_Error.html
Copyright © 2020-2023  润新知