Dynamics 365 CRM Online - 插件仅在每隔一次被触发时执行更新

Dynamics 365 CRM Online - Plugin Only Performs Update Every Other Time it is Triggered

更新 我从这个论坛以及 Dynamics 论坛中提取了一些信息,并且我的代码可以正常工作。我的代码中的主要问题是我只查看了 preImage,但在大多数情况下,三个值之一将被更新,这意味着我无法针对该特定字段的 preImage。我更改了我的逻辑以在更新时使用 Collection 属性(更新值),但在未更新时使用 PreImage 值 - 这使代码工作,并且每次都会触发更新 - 不再需要触发插件两次!这是更新后的代码 - 比原始代码更高效和简洁(向下滚动以查看 old/non 工作代码)。我还添加了一些注释以使其更容易理解。感谢大家的帮助(在 SO 和 Dynamics 论坛上!)

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace CLIENTNTE
{
    public class NTEExceedance : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));


            Decimal nte_percent = 0;
            Decimal subtotalDecimal = 0;
            Decimal nteDecimal = 0;
            Decimal amountDiffDecimal = 0;
            Decimal percentDifference = 0;
            try
            {

                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity entity = (Entity)context.InputParameters["Target"];
                    //if entity is not Work Order, return.  Prevents plugin firing on wrong entity (in case of wrong registration in plugin registration tool)
                    if (entity.LogicalName != "msdyn_workorder")
                    {
                        return;
                    }
                    //get preimage of WO Entity
                    Entity preMessageImage = (Entity)context.PreEntityImages["WONTEPreImage"];

                    //logic for when updated attribute is NTE Amount
                    if (entity.Attributes.Contains("CLIENT_nteamount") == true)
                    {
                        nteDecimal = entity.GetAttributeValue<Money>("CLIENT_nteamount").Value;
                    }
                    else
                    {
                        nteDecimal = preMessageImage.GetAttributeValue<Money>("CLIENT_nteamount").Value;
                    }
                    //logic for when updated attribute is NTE Percent
                    if (entity.Attributes.Contains("CLIENT_ntepercent") == true)
                    {
                        nte_percent = entity.GetAttributeValue<Decimal>("CLIENT_ntepercent");
                    }
                    else
                    {
                        nte_percent = preMessageImage.GetAttributeValue<Decimal>("CLIENT_ntepercent");
                    }
                    //logic for when updated attribute is Estimate Subtotal Amount
                    if (entity.Attributes.Contains("msdyn_estimatesubtotalamount") == true)
                    {
                        subtotalDecimal = entity.GetAttributeValue<Money>("msdyn_estimatesubtotalamount").Value;
                    }
                    else
                    {
                        subtotalDecimal = preMessageImage.GetAttributeValue<Money>("msdyn_estimatesubtotalamount").Value;
                    }

                    //calculation of Amount Difference, and Percent Difference
                    amountDiffDecimal = (subtotalDecimal - nteDecimal);
                    percentDifference = ((amountDiffDecimal / nteDecimal) * 100);

                    //Comparison logic to update the NTE Exceeded flag
                    if (percentDifference > nte_percent)
                    {
                        entity["CLIENT_nteexceeded"] = true;
                    }
                    if (percentDifference <= nte_percent)
                    {
                        entity["CLIENT_nteexceeded"] = false;
                    }
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                //write errors to the CRM Plugin Trace Log
                tracingService.Trace("CLIENTPlugin - Update NTEExceededNonCalc: {0}", e.ToString());
                //Throw error through UI
                throw new InvalidPluginExecutionException("Error, Please See Plugin Log");
            }
        }
    }
}

原版Post 再来一次!昨天,我意识到我没有使用 PreImage 来获取未包含在更新的属性集合中的值的方式的错误 - SCORE!现在,插件 工作 ,但它仅在我每隔一次触发插件时工作。

所以,插件似乎确实在启动(我可以在“设置”>“插件配置文件”中看到插件配置文件),但没有执行我需要的更新,直到我第二次触发插件。这似乎发生在我正在监听的所有 3 个属性上,并且每个属性都需要更新两次(例如,如果我更新属性 1,插件不会更新我的值,如果我随后更新属性 2,插件仍然不会更新我的值,直到我再次重新更新属性 1 或属性 2)。当我 profile/debug 我的代码并逐步执行它时,我可以看到 if 语句被命中,并且更新实体字段的代码行也被执行 - 但无论出于何种原因,它都没有设置value (CLIENT_nteexceeded = true or false) 直到我第二次触发它。如果它根本不更新实体记录值,那将更有意义,而且我遗漏了某种类型的 'update' 消息(我玩过 service.Entity.Update() 但确实如此似乎不适用于此插件)

我对 CRMDEV 还是比较陌生,所以请原谅这个愚蠢的问题。这对我来说似乎是一个非常愚蠢的问题,而且它一定是我遗漏的小问题。关于为什么这会迫使我触发代码两次以执行更新有什么想法吗?

这是插件步骤的配置。三个过滤器属性是代码中引用的那些。同样,插件工作正常,只是不会每次都更新记录。

下面的代码(删除了对客户名称的引用)

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace CLIENTNTE
{
    public class NTEExceedance : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);
            //Extract the tracing service for use in debugging sandboxed plug-ins.
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            Money subtotal = null;
            Money nte = null;
            Decimal nte_percent = 0;
            Decimal subtotalDecimal = 0;
            Decimal nteDecimal = 0;
            Decimal amountDiffDecimal = 0;
            Decimal percentDifference = 0;
            try
            {

                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity entity = (Entity)context.InputParameters["Target"];
                    if (entity.LogicalName == "msdyn_workorder")
                    {
                        //code fires onChange of NTE Amount (same logic will apply to NTE % and Est Subtotal Amount)
                        if (entity.Attributes.Contains("CLIENT_nteamount") == true)
                        {
                            //String NewValue = FieldValue(service, new Guid(entity["msdyn_workorderid"].ToString()));
                            // String NewSubTotal = FieldValue(service, new Guid(entity["msdyn_workorderid"].ToString()), entity["msdyn_estimatesubtotalamount"].ToString());
                            //String NewNTE = FieldValue(service, new Guid(entity["msdyn_workorderid"].ToString()), entity["CLIENT_nteamount"].ToString());
                            //String Newpercent = FieldValue(service, new Guid(entity["msdyn_workorderid"].ToString()), entity["CLIENT_ntepercent"].ToString());
                            if (context.PreEntityImages.Contains("WONTEPreImage") && context.PreEntityImages["WONTEPreImage"] is Entity)
                            {
                                Entity preMessageImage = (Entity)context.PreEntityImages["WONTEPreImage"];
                                // get topic field value before database update perform
                                //pretopic = (String)preMessageImage.Attributes["subject"];
                                subtotal = (Money)preMessageImage.Attributes["msdyn_estimatesubtotalamount"];
                                nte = (Money)preMessageImage.Attributes["CLIENT_nteamount"];
                                nte_percent = (Decimal)preMessageImage.Attributes["CLIENT_ntepercent"];
                            }
                            //old way of trying to get values NON IMAGE
                            //subtotal = (Money)entity.Attributes["msdyn_estimatesubtotalamount"];
                            //nte = (Money)entity.Attributes["CLIENT_nteamount"];
                            //nte_percent = (Decimal)entity.Attributes["CLIENT_ntepercent"];

                            subtotalDecimal = subtotal.Value;
                            nteDecimal = nte.Value;
                            amountDiffDecimal = (subtotalDecimal - nteDecimal);
                            percentDifference = ((amountDiffDecimal / nteDecimal) * 100);
                            if (percentDifference > nte_percent)
                            {
                                //know this snippet works
                                entity["CLIENT_nteexceeded"] = true;
                            }
                            if (percentDifference <= nte_percent)
                            {
                                //know this snippet works
                                entity["CLIENT_nteexceeded"] = false;
                            }
                        }
                        if (entity.Attributes.Contains("CLIENT_ntepercent") == true)
                        {
                            if (context.PreEntityImages.Contains("WONTEPreImage") && context.PreEntityImages["WONTEPreImage"] is Entity)
                            {
                                Entity preMessageImage = (Entity)context.PreEntityImages["WONTEPreImage"];
                                // get topic field value before database update perform
                                //pretopic = (String)preMessageImage.Attributes["subject"];
                                subtotal = (Money)preMessageImage.Attributes["msdyn_estimatesubtotalamount"];
                                nte = (Money)preMessageImage.Attributes["CLIENT_nteamount"];
                                nte_percent = (Decimal)preMessageImage.Attributes["CLIENT_ntepercent"];
                            }

                            //old way of trying to get values NON IMAGE
                            //subtotal = (Money)entity.Attributes["msdyn_estimatesubtotalamount"];
                            //nte = (Money)entity.Attributes["CLIENT_nteamount"];
                            //nte_percent = (Decimal)entity.Attributes["CLIENT_ntepercent"];
                            subtotalDecimal = subtotal.Value;
                            nteDecimal = nte.Value;
                            amountDiffDecimal = (subtotalDecimal - nteDecimal);
                            percentDifference = ((amountDiffDecimal / nteDecimal) * 100);

                            if (percentDifference > nte_percent)
                            {
                                //know this snippet works
                                entity["CLIENT_nteexceeded"] = true;
                            }
                            if (percentDifference <= nte_percent)
                            {
                                //know this snippet works
                                entity["CLIENT_nteexceeded"] = false;
                            }
                        }
                        if (entity.Attributes.Contains("msdyn_estimatesubtotalamount") == true)
                        { 
                            if (context.PreEntityImages.Contains("WONTEPreImage") && context.PreEntityImages["WONTEPreImage"] is Entity)
                            {
                                Entity preMessageImage = (Entity)context.PreEntityImages["WONTEPreImage"];
                                subtotal = (Money)preMessageImage.Attributes["msdyn_estimatesubtotalamount"];
                                nte = (Money)preMessageImage.Attributes["CLIENT_nteamount"];
                                nte_percent = (Decimal)preMessageImage.Attributes["CLIENT_ntepercent"];
                            }

                            //old way of trying to get values NON IMAGE
                            //subtotal = (Money)entity.Attributes["msdyn_estimatesubtotalamount"];
                            //nte = (Money)entity.Attributes["CLIENT_nteamount"];
                            //nte_percent = (Decimal)entity.Attributes["CLIENT_ntepercent"];

                            subtotalDecimal = subtotal.Value;
                            nteDecimal = nte.Value;
                            amountDiffDecimal = (subtotalDecimal - nteDecimal);
                            percentDifference = ((amountDiffDecimal / nteDecimal) * 100);

                            if (percentDifference > nte_percent)
                            {
                                //know this snippet works
                                entity["CLIENT_nteexceeded"] = true;
                            }
                            if (percentDifference <= nte_percent)
                            {
                                //know this snippet works
                                entity["CLIENT_nteexceeded"] = false;
                            }

                        }

                    }
                }

            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                tracingService.Trace("CLIENTPlugin - Update NTEExceededNonCalc: {0}", e.ToString());
                throw e;
            }
        }
    }

}

我将帮助您了解 CRM 执行管道的工作原理。然后,您可以在 trace/Audit/profiler 的帮助下进行故障排除,以确定故障所在。

基本上,您是在 pre-validation/pre-operation 阶段的数据库事务之前拦截从 CRM UI 发生的 service.Update() 平台调用。然后根据业务需要进行数据操作并在目标实体本身中设置额外的属性值。这避免了另一个明确的额外 service.Update() 调用 & 反过来循环死锁。

如果有任何异常,您正在登录跟踪并抛出相同的异常。我建议将其更改为 InvalidPluginExecutionException,您将在 UI 弹出窗口 window 中看到它。这将回滚事务,这意味着数据不会被保存。

此外,为了更好地理解 - 记录每个条件语句、变量值等,以查看出错的确切跟踪日志。

Update/Answer 我从这个论坛以及 Dynamics 论坛中提取了一些信息,我的代码开始工作了。我的代码中的主要问题是我只查看了 preImage,但在大多数情况下,三个值之一将被更新,这意味着我无法针对该特定字段的 preImage。我更改了我的逻辑以在更新时使用 Collection 属性(更新值),但在未更新时使用 PreImage 值 - 这使代码工作,并且每次都会触发更新 - 不再需要触发插件两次!这是更新后的代码 - 比原始代码更高效和简洁(向下滚动以查看 old/non 工作代码)。我还添加了一些注释以使其更容易理解。感谢大家的帮助(在 SO 和 Dynamics 论坛上!)

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace CLIENTNTE
{
    public class NTEExceedance : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));


            Decimal nte_percent = 0;
            Decimal subtotalDecimal = 0;
            Decimal nteDecimal = 0;
            Decimal amountDiffDecimal = 0;
            Decimal percentDifference = 0;
            try
            {

                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity entity = (Entity)context.InputParameters["Target"];
                    //if entity is not Work Order, return.  Prevents plugin firing on wrong entity (in case of wrong registration in plugin registration tool)
                    if (entity.LogicalName != "msdyn_workorder")
                    {
                        return;
                    }
                    //get preimage of WO Entity
                    Entity preMessageImage = (Entity)context.PreEntityImages["WONTEPreImage"];

                    //logic for when updated attribute is NTE Amount
                    if (entity.Attributes.Contains("CLIENT_nteamount") == true)
                    {
                        nteDecimal = entity.GetAttributeValue<Money>("CLIENT_nteamount").Value;
                    }
                    else
                    {
                        nteDecimal = preMessageImage.GetAttributeValue<Money>("CLIENT_nteamount").Value;
                    }
                    //logic for when updated attribute is NTE Percent
                    if (entity.Attributes.Contains("CLIENT_ntepercent") == true)
                    {
                        nte_percent = entity.GetAttributeValue<Decimal>("CLIENT_ntepercent");
                    }
                    else
                    {
                        nte_percent = preMessageImage.GetAttributeValue<Decimal>("CLIENT_ntepercent");
                    }
                    //logic for when updated attribute is Estimate Subtotal Amount
                    if (entity.Attributes.Contains("msdyn_estimatesubtotalamount") == true)
                    {
                        subtotalDecimal = entity.GetAttributeValue<Money>("msdyn_estimatesubtotalamount").Value;
                    }
                    else
                    {
                        subtotalDecimal = preMessageImage.GetAttributeValue<Money>("msdyn_estimatesubtotalamount").Value;
                    }

                    //calculation of Amount Difference, and Percent Difference
                    amountDiffDecimal = (subtotalDecimal - nteDecimal);
                    percentDifference = ((amountDiffDecimal / nteDecimal) * 100);

                    //Comparison logic to update the NTE Exceeded flag
                    if (percentDifference > nte_percent)
                    {
                        entity["CLIENT_nteexceeded"] = true;
                    }
                    if (percentDifference <= nte_percent)
                    {
                        entity["CLIENT_nteexceeded"] = false;
                    }
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                //write errors to the CRM Plugin Trace Log
                tracingService.Trace("CLIENTPlugin - Update NTEExceededNonCalc: {0}", e.ToString());
                //Throw error through UI
                throw new InvalidPluginExecutionException("Error, Please See Plugin Log");
            }
        }
    }
}