• 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并设置回自动编号设置记录。这样就可以了。

  • 相关阅读:
    PAT 解题报告 1009. Product of Polynomials (25)
    PAT 解题报告 1007. Maximum Subsequence Sum (25)
    PAT 解题报告 1003. Emergency (25)
    PAT 解题报告 1004. Counting Leaves (30)
    【转】DataSource高级应用
    tomcat下jndi配置
    java中DriverManager跟DataSource获取getConnection有什么不同?
    理解JDBC和JNDI
    JDBC
    Dive into python 实例学python (2) —— 自省,apihelper
  • 原文地址:https://www.cnblogs.com/luoyong0201/p/Dynamics_365_scalable_customization_design_auto_numbering_example.html
Copyright © 2020-2023  润新知