zoukankan      html  css  js  c++  java
  • Dynamics 365中定制包括一个流水号但不会产生重复编号的功能

    我是微软Dynamics 365 & Power Platform方面的工程师/顾问罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP),欢迎关注我的微信公众号 MSFTDynamics365erLuoYong ,回复433或者20210131可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!

    有的项目喜欢编号,当然简单的编号可以考虑使用标准的自动编号功能,可以参考我之前的博文 Dynamics 365 Customer Engagement V9.X新引入的自动编号属性介绍 。

    我今天要做个客制化,需求是每月从1开始编号,希望编号不会重复,是否连续没有那么重要,如何做呢?

    可以考虑这个官方文档 Scalable Customization Design: Auto-numbering example 提供的思路,大致的做法就是利用锁来做,同时利用一个辅助字段。我这里不多解释了,请参考官方文档。

    我具体做法是使用了一个插件,注册在需要生成编号的实体的Create消息的Pre Operation阶段,设置如下:

    这个使用的代码如下:

    using System;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Query;
    
    namespace CRM.Plugins
    {
        public class TestPreCreate : IPlugin
        {
            public void Execute(IServiceProvider serviceProvider)
            {
                ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity currentEntity = (Entity)context.InputParameters["Target"];
                    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    //对编号设置信息的读取更改使用SYSTEM账号进行
                    IOrganizationService orgAdminSvc = serviceFactory.CreateOrganizationService(null);
                    //IOrganizationService orgSvc = serviceFactory.CreateOrganizationService(context.UserId);
                    var groupName = DateTime.UtcNow.AddHours(8).Year.ToString() + DateTime.UtcNow.AddHours(8).Month.ToString().PadLeft(2, '0');
                    string fetchXml = string.Format(@"<fetch version='1.0' top='1' mapping='logical' distinct='false'>
      <entity name='ly_autonumber'>
        <attribute name='ly_autonumberid' />
        <filter type='and'>
          <condition attribute='ly_name' operator='eq' value='{0}' />
          <condition attribute='statecode' operator='eq' value='0' />
        </filter>
      </entity>
    </fetch>", groupName);
                    var autonumberEC = orgAdminSvc.RetrieveMultiple(new FetchExpression(fetchXml));
                    if (autonumberEC.Entities.Count == 1)
                    {
                        autonumberEC.Entities[0]["ly_subsidiaryfield"] = DateTime.UtcNow.Ticks.ToString();
                        orgAdminSvc.Update(autonumberEC.Entities[0]);
                        //再查一次当前编号很重要,否则会出现重复编号
                        var currentStep = orgAdminSvc.Retrieve(autonumberEC.Entities[0].LogicalName, autonumberEC.Entities[0].Id,new ColumnSet("ly_currentstep")).GetAttributeValue<int>("ly_currentstep");
                        autonumberEC.Entities[0]["ly_currentstep"] = currentStep + 1;
                        currentEntity["ly_no"] = $"{groupName}-{(currentStep + 1).ToString().PadLeft(6, '0')}";
                        orgAdminSvc.Update(autonumberEC.Entities[0]);
                    }
                    else
                    {
                        throw new InvalidPluginExecutionException("请联系系统管理员先配置好分组!");
                    }
                }
            }
        }
    }

    然后如何测试呢?我这里启动5个程序,每个程序用 ExecuteMultipleRequest 消息向Dynamics 365来创建500条记录,使用的代码如下:

       class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    Console.WriteLine($"要开始,输入Y继续");
                    var input = Console.ReadLine().ToString().ToUpper();
                    if (input == "Y")
                    {
                        CrmServiceClient.MaxConnectionTimeout = new TimeSpan(10, 0, 0);
                        CrmServiceClient crmSourceSvc = new CrmServiceClient(ConfigurationManager.AppSettings["ConnStr"]);
                        if (!crmSourceSvc.IsReady)
                        {
                            throw new Exception("连接ConnStr失败!" + crmSourceSvc.LastCrmError);
                        }
                        else
                        {
                            Console.WriteLine($"程序连接到Dynamics 365/Power Apps环境 {crmSourceSvc.ConnectedOrgFriendlyName } 成功!");
                        }
                        ExecuteMultipleResponse multiRep;
                        ExecuteMultipleRequest multiReqs = new ExecuteMultipleRequest()
                        {
                            Settings = new ExecuteMultipleSettings()
                            {
                                ContinueOnError = true,
                                ReturnResponses = true
                            },
                            Requests = new OrganizationRequestCollection()
                        };
                        for (int i = 0; i < 500; i++)
                        {
                            CreateRequest req = new CreateRequest();
                            var createEntity = new Entity("new_testentity");
                            createEntity["new_name"] = DateTime.UtcNow.AddHours(8).Ticks.ToString();
                            req.Target = createEntity;
                            multiReqs.Requests.Add(req);
                        }
                        multiRep = (ExecuteMultipleResponse)crmSourceSvc.Execute(multiReqs);
                        foreach (var Rep in multiRep.Responses)
                        {
                            if (Rep.Fault != null)
                            {
                                Console.WriteLine(Rep.Fault.Message);
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("你选择了取消程序运行!");
                    }
                    Console.WriteLine("程序运行完成,按任意键退出!");
                    Console.ReadKey();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("程序运行出错:" + ex.Message + ex.StackTrace);
                    Console.ReadLine();
                }
            }
        }

    然后我进行了测试,可以看到生成了 2500条记录,哪怕是同时开始创建的记录,生成的编号也不一样,而且是顺序的。

    从Auto Number设置的Audit History来看,序号是连续生成的。

    值得注意的是,这种方法需要生成的代码执行比较快才好,否则容易给人卡顿的感觉,而且在高并发情况下,因为等待过久导致超时也会出现(一个请求执行完毕不能超过2分钟,若超过就会报错)。

    这个是因为所以的代码都在同一个事务中,产生新的编号前先update编号设置记录,这时候会锁住记录,然后快速生成下一个编号(此时特别注意的是需要再次查询下当前流水号),复制给当前实体的编号字段,然后创建成功,创建成功后事务结束,对编号设置记录的锁也就释放,其他排队的请求就会接上继续生成。

    你可能会问,这个是因为在同一个事务中,如果不在同一个事务中呢?比如是利用异步插件或者自定义工作流活动的代码呢?

    那怎么办?没有条件创造条件也要上啊,比如创建事务出来,官方不推荐在插件代码或者自定义工作流活动代码中使用 ExecuteTransactionRequest 来构造。

    那我的办法就是找到更新找到的自动编号设置记录后,更新它。我在自动编号设置实体的Update消息上注册一个Pre Opreation的插件(这个代码执行与主操作在同一个事务中,属于同一个事务),监控要更新的字段,我这里一般用一个辅助字段,使用一个Pre Image将这个当前顺序号的值传递给插件代码,在插件代码中处理逻辑并为记录设置新的编号,将顺序号加1并设置回自动编号设置记录。这样就可以了。

  • 相关阅读:
    【2019.7.26 NOIP模拟赛 T1】数字查找(figure)(数学)
    【2019.7.25 NOIP模拟赛 T1】变换(change)(思维+大分类讨论)
    简析平衡树(四)——FHQ Treap
    【BZOJ3529】[SDOI2014] 数表(莫比乌斯反演)
    【洛谷1829】 [国家集训队] Crash的数字表格(重拾莫比乌斯反演)
    【PE512】Sums of totients of powers(欧拉函数)
    【CFGym102059G】Fascination Street(思维DP)
    【CF438D】The Child and Sequence(线段树)
    【2019.7.16 NOIP模拟赛 T2】折叠(fold)(动态规划)
    【UVA1303】Wall(凸包)
  • 原文地址:https://www.cnblogs.com/luoyong0201/p/Dynamics_365_scalable_customization_design_auto_numbering_example.html
Copyright © 2011-2022 走看看