【问题标题】:Working on a Dynamics CRM Custom Workflow that updates "Modified By" field -- need help debugging处理更新“修改者”字段的 Dynamics CRM 自定义工作流——需要帮助调试
【发布时间】:2019-06-29 20:54:32
【问题描述】:

我正在开发一个 Dynamics CRM CWA,它会根据名为“准备者”的文本字段更新“修改者”字段。我目前有 3 个错误,我需要一些帮助调试(见下文)。它们可能很容易修复,但我对编码相当陌生。任何帮助调试将不胜感激。谢谢!

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Threading.Tasks;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System.Runtime.Serialization;

namespace KED365.Workflows
{


    /// </summary>    
    public class ModifiedBy : WorkFlowActivityBase
    {
        private Guid contactid;

        [Input("User Full Name")]
        public InArgument<string> UserFullName { get; set; }


        /// <summary>
        /// Executes the WorkFlow.
        /// </summary>
        /// <param name="crmWorkflowContext">The <see cref="LocalWorkflowContext"/> which contains the
        /// <param name="executionContext" > <see cref="CodeActivityContext"/>
        /// </param>       
        /// <remarks>
        /// For improved performance, Microsoft Dynamics 365 caches WorkFlow instances.
        /// The WorkFlow's Execute method should be written to be stateless as the constructor
        /// is not called for every invocation of the WorkFlow. Also, multiple system threads
        /// could execute the WorkFlow at the same time. All per invocation state information
        /// is stored in the context. This means that you should not use global variables in WorkFlows.
        /// </remarks>
        protected override void Execute(CodeActivityContext activityContext, IWorkflowContext workflowContext, IOrganizationService orgService, ITracingService tracingService)
        {
            //get entity record for which plugin was fired
            Entity _target = (Entity)workflowContext.InputParameters["Target"];


            //check if portaluser name is to be obtained from custom createby or from custom modifiedby
            if (workflowContext.MessageName.ToUpper() == "CREATE")
            {
                contactid = _target.Attributes.Contains("new_createdby") ? _target.GetAttributeValue<EntityReference>("new_createdby").Id : Guid.Empty;
            }
            else
            {
                contactid = _target.Attributes.Contains("new_modifiedby") ? _target.GetAttributeValue<EntityReference>("new_modifiedby").Id : Guid.Empty;
            }

            //retrieve contact fullname from contactid
            var _contact = activityContext.CreateQuery("contact").Where(c => c.GetAttributeValue<Guid>("contactid").Equals(contactid)).FirstOrDefault();


            if (_contact != null)
            {
                if (_contact.Attributes.Contains("fullname"))
                {
                    fullname = _contact.GetAttributeValue<string>("fullname");
                }

                //retrieve Systemuser that has same name as that of new_portalcreatedby/ //new_portalmodifiedby
                Entity _user = context.CreateQuery("systemuser").Where(e => e.GetAttributeValue<string>("fullname").Equals(fullname)).FirstOrDefault();

                if (_user != null)
                {

                    //check if we need to update createdby or modifiedby
                    if (workflowContext.MessageName.ToUpper() == "CREATE")
                    {
                        _target["createdby"] = _user.ToEntityReference();
                    }
                    else
                    {
                        _target["modifiedby"] = _user.ToEntityReference();
                    }

                    //assign new target to plugin executioncontext
                    workflowContext.InputParameters["Target"] = _target;
                }
            }
        }
    }
}

错误 1:

严重性代码描述项目文件行抑制状态 错误 CS1061“CodeActivityContext”不包含“CreateQuery”的定义,并且找不到接受“CodeActivityContext”类型的第一个参数的扩展方法“CreateQuery”(您是否缺少 using 指令或程序集引用?)工作流 C:\用户\tgiard\Downloads\GetUserByName-master\GetUserByName-master\Workflows\ModifiedBy.cs 68 活动

错误 2:

严重性代码描述项目文件行抑制状态 错误 CS0103 当前上下文中不存在名称“全名”工作流 C:\Users\tgiard\Downloads\GetUserByName-master\GetUserByName-master\Workflows\ModifiedBy.cs 75 活动

错误 3:

严重性代码描述项目文件行抑制状态 错误 CS0103 当前上下文中不存在名称“上下文”工作流 C:\Users\tgiard\Downloads\GetUserByName-master\GetUserByName-master\Workflows\ModifiedBy.cs 79 活动

【问题讨论】:

  • 很好奇你为什么使用 CWA 而不是普通的插件。您已经定义了一个 InputParameter,但您没有在代码中的任何地方使用它。自定义工作流活动的指导是您应该定义输入和输出参数,以便在设计器中使用自定义活动的人可以看到它在做什么。您可能希望使用自定义活动的输出来更新该值,而不是直接使用 Target 参数。 docs.microsoft.com/en-us/powerapps/developer/…
  • @JimDaly-MSFT-,CWA和普通插件有什么区别?我的印象是它们是同一回事。另外,关于我应该在哪里使用 InputParameter 有什么建议吗?这对我来说都是全新的,如果我遗漏了一些明显的东西,我深表歉意。谢谢!
  • 推荐您从这里开始:docs.microsoft.com/en-us/powerapps/developer/… 请记住,CRM 使用现在称为 Common Data Service 的平台。自定义工作流组件,也称为工作流扩展,旨在提供 UI 以扩展工作流设计器。插件是为事件注册的,没有 UI。
  • 我同意 Jim,这段代码应该在插件中

标签: dynamics-crm workflow


【解决方案1】:

以下是对您的问题的一些反馈:

错误 1 - “CodeActivityContext”不包含“CreateQuery”的定义
此问题与以下几行有关:

var _contact = activityContext.CreateQuery("contact").Where(c => c.GetAttributeValue<Guid>("contactid").Equals(contactid)).FirstOrDefault();

Entity _user = context.CreateQuery("systemuser").Where(e => e.GetAttributeValue<string>("fullname").Equals(fullname)).FirstOrDefault();

我不知道那个方法是什么,但你有更好的选择;对于您已经拥有 guid 的联系人,您可以简单地使用 Retrieve():

    var _contact = orgService.Retrieve("contact", contactid, new ColumnSet("fullname"));

并为系统用户编写一个按全名过滤的QueryExpression:

var query = new QueryExpression("systemuser"):
query.Criteria.AddCondition("fullname", ConditionOperator.Equal, fullname);
var _user = orgService.RetrieveMultiple(query).Entities.FirstOrDefault();

错误 2: 名称 'fullname' 在当前上下文中不存在
这是基本的 C#,你必须在使用它之前实例化你的变量:

string fullname;

错误 3: 当前上下文中不存在名称“context”
讽刺而真实。这应该是 activityContext,但我们已经在对错误 1 ​​所做的更改中修复了这个问题。

Entity _user = context.CreateQuery("systemuser").Where(e => e.GetAttributeValue<string>("fullname").Equals(fullname)).FirstOrDefault();

【讨论】:

  • 谢谢@Zach!使用您的建议,我能够摆脱每个错误。我将自定义工作流步骤部署到 CRM 中,但在运行工作流时出现以下错误: SandboxFault.ThrowIfGuidEmpty: entityId 关于如何解决此问题的任何想法?当“准备者”字段更新时,我的工作流程就会被触发。然后,我使用自定义工作流程步骤,并将“准备者”字段中的任何内容作为此步骤的输入。
  • 我认为与其尝试解决此问题,不如按照 Jim 的建议重新编写代码以在预创建/预更新插件中运行。大部分实际代码将保持不变。
【解决方案2】:

正如 Zach Mast 正确指出的那样,建议使用预操作。您检索联系人姓名并将其与用户匹配似乎也是一种奇怪的情况。相反,您可以将字段的类型更改为用户参考,将用户字段添加到您检索的联系人或添加代码以将联系人与用户匹配。这样,您就不会遇到同名或拼写错误的用户的问题。

请在下方找到您转换为 Pre-Operation 插件的工作流活动。

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Linq;

namespace KED365.Plugins
{
    public class CreateUpdateContact : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            var service = factory.CreateOrganizationService(context.UserId);
            tracingService.Trace("Start plugin");

            tracingService.Trace("Validate Target");
            if (!context.InputParameters.Contains("Target") || !(context.InputParameters["Target"] is Entity))
                return;

            tracingService.Trace("Retrieve Target");
            var target = (Entity)context.InputParameters["Target"];

            String message = context.MessageName.ToLower();

            SetCreatedByAndModifiedBy(tracingService, service, target, message);
        }

        private void SetCreatedByAndModifiedBy(ITracingService tracingService, IOrganizationService service, Entity target, string message)
        {
            tracingService.Trace("Start SetPriceList");
            tracingService.Trace("Validate Message is Create or Update");
            if (!message.Equals("create", StringComparison.OrdinalIgnoreCase) && !message.Equals("update", StringComparison.OrdinalIgnoreCase))
                return;

            tracingService.Trace("Retrieve Attributes");
            var createdByReference = target.GetAttributeValue<EntityReference>("new_createdby");
            var modifiedByReference = target.GetAttributeValue<EntityReference>("new_modifiedby");

            tracingService.Trace("Retrieve And Set User for Created By");
            RetrieveAndSetUser(tracingService, service, target, createdByReference, "createdby");

            tracingService.Trace("Retrieve And Set User for Modified By");
            RetrieveAndSetUser(tracingService, service, target, modifiedByReference, "modifiedby");
        }

        private void RetrieveAndSetUser(ITracingService tracingService, IOrganizationService service, Entity target, EntityReference reference, string targetAttribute)
        {
            tracingService.Trace("Validating Reference");
            if (reference == null)
                return;

            tracingService.Trace("Retrieving and Validating User");
            var user = RetrieveUserByName(service, reference.Name, new ColumnSet(false));
            if (user == null)
                return;

            tracingService.Trace("Setting Target Attribute");
            target[targetAttribute] = user.ToEntityReference();
        }

        private Entity RetrieveUserByName(IOrganizationService service, string name, ColumnSet columns)
        {
            var query = new QueryExpression
            {
                EntityName = "systemuser",
                ColumnSet = columns,
                Criteria = new FilterExpression
                {
                    FilterOperator = LogicalOperator.And,
                    Conditions =
                    {
                        new ConditionExpression
                        {
                            AttributeName = "fullname",
                            Operator = ConditionOperator.Equal,
                            Values = { name }
                        }
                    }
                }
            };

            var retrieveResponse = service.RetrieveMultiple(query);
            if (retrieveResponse.Entities.Count == 1)
            {
                return retrieveResponse.Entities.FirstOrDefault();
            }
            else
            {
                // Alternatively you can thrown an error as you have unexpectedly multiple matches
                return null;
            }
        }
    }
}

【讨论】:

  • 感谢您的帮助!我能够注册预操作插件。但是,“修改者”字段仍未更新。注册后,我是否需要创建工作流程或其他内容?或者插件应该在注册后自动运行吗?此外,“准备者”字段会自动填充,但通常会在记录创建后几秒钟发生。这可能是“修改者”字段没有更新的原因吗?最后,我想知道如果我使用预验证插件而不是预操作插件是否会有所帮助?
  • 您好 Tim,请在系统设置中打开插件跟踪并查看 PluginTrace(Dynamics CRM 中的实体)以查看插件执行或未执行的操作。我认为由于我的代码中的错误或名称不匹配,它找不到匹配项。需要调试它,它应该可以工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多