zoukankan      html  css  js  c++  java
  • Dynamics 365 Customer Engagement的标准导入不支持并行导入了吗?

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

    利用标准的导入功能来导入数据是基本操作,操作起来简单引用,比较受用户喜欢,而且导入速度也可以,特别是并行导入的情况下。

    最近碰到用CSV来导入数据,在导入之前将数据拆分成多个CSV文件,每个文件不超过8M,这些CSV文件打包成Zip文件后不超过32MB,上传后是差不多一个一个处理,并行处理没有那么明显了。如下图,我最开始用一个csv上传了一个文件进行导入,后面我用一个zip包上传了5个csv文件来导入,这个zip文件中的5个文件要会等到第一个处理完毕后再开始。然后zip中的文件有些并行处理,但是看起来不明显,下图可以看到 data02.csv 和 data03.csv都处于解析阶段。

    我的印象中,Dynamics 365 CRM V8.X是可以并行导入的,而且处理速度挺快的,是Dynamics 365 Customer Engagement V9.X开始因为调用API的限制等并行不是那么厉害了吗?如果是,谁看到哪儿有说明请告知我,我更新下本博文,可由什么参数可以更改此类设置。

    我查看了导入相关的部署属性,参考 Update deployment configuration settings ,查看的命令参考如下:

    Add-PSSnapin Microsoft.Crm.PowerShell
    Get-CrmSetting ImportSettings

    我的环境显示结果如下,这些值应该是默认值。

    如果是一个文件一个文件导入,速度不会很快,我们可以用程序来导入,多线程加上ExecuteMultipleRequest消息速度还是可以的。

    • 导入文件的文件名尽量用字母数字,不要包括空格,汉字,特殊符号等。
    • CSV文件的编码是UTF-8,而不是ANSI,因为ANSI编码不支持中文,导入后会发现中文是乱码!
    • 导入尽量用CSV格式,而不是Excel。
    • 对于有1:N关系的实体导入,最好能从源头生成GUID作为父实体的主键,然后导入子实体的时候直接利用前面生成的GUID,这样处理速度会快很多。
    • 对于每行记录最好有一个惟一键,如果没有可以用自增列(或者Excel生成一个序号)或者GUID作为惟一键,这样方便后续或者后台比较数据导入的成果。
    • 如果导入消息执行非常快,可能会碰上调用API的限制,调用API的限制请参考官方文档:API Limits ,这个时候通过ExecuteMultipleRequest消息和使用不同的账号来执行可以降低概率。
    • 如果可以,为每个要导入的文件准备一个整数,作为导入记录的 importsequencenumber 字段值,方便分析每个文件导入数据的情况。
    • 最好每个文件的行数不要超过10万,因为情况下导出记录到Excel的条数是10万。
    • 导入的程序最好放到服务器上运行,提升连接速度,同时也一般不容易像自己的机器一样关机或者重启会中断导入

    我这里贴一个导入的代码:

    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using Microsoft.Xrm.Sdk.Messages;
    using System;
    using System.Configuration;
    using System.IO;
    using System.ServiceModel;
    using System.Threading;
    
    namespace BulkImportRecords
    {
        class Program
        {
            public static IServiceManagement<IOrganizationService> sm;
            public static AuthenticationCredentials authCredentials;
            static int importsequencenumberstartat = Convert.ToInt32(ConfigurationManager.AppSettings["importsequencenumberstartat"]);
            static int threadcount = Convert.ToInt32(ConfigurationManager.AppSettings["threadcount"]);
            static void Main(string[] args)
            {
                sm = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(ConfigurationManager.AppSettings["orgUrl"]));
                authCredentials = new AuthenticationCredentials();
                authCredentials.ClientCredentials.UserName.UserName = ConfigurationManager.AppSettings["userName"];
                authCredentials.ClientCredentials.UserName.Password = ConfigurationManager.AppSettings["passWord"];
                authCredentials = sm.Authenticate(authCredentials);
                try
                {
                    for (var i = 0; i < threadcount; i++)
                    {
                        Thread newThread = new Thread(new ParameterizedThreadStart(Work));
                        newThread.Start(i);
                    }
                    Console.ReadKey();
                }
                catch (FaultException ex)
                {
                    Console.WriteLine("程序出现异常:ex.Message=" + ex.Message);
                    Console.ReadKey();
                }
            }
    
            static void Work(object data)
            {
                try
                {
                    Console.WriteLine("线程开始" + DateTime.Now.ToLongTimeString() + ";线程ID:" + Thread.CurrentThread.ManagedThreadId + ";接收的参数值为:" + data.ToString());
                    int importsequencenumber = importsequencenumberstartat + Convert.ToInt32(data);
                    OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(sm, authCredentials.ClientCredentials);
                    //OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(sm, authCredentials.SecurityTokenResponse);
                    //ManagedTokenOrganizationServiceProxy orgSvc = new ManagedTokenOrganizationServiceProxy(sm, authCredentials.ClientCredentials);
                    string strReadFilePath = ConfigurationManager.AppSettings["filename"];
                    int i = 0;
                    int j = 1;
                    int z = 1;
                    ExecuteMultipleRequest multiReqs = new ExecuteMultipleRequest()
                    {
                        Settings = new ExecuteMultipleSettings()
                        {
                            ContinueOnError = true,
                            ReturnResponses = false
                        },
                        Requests = new OrganizationRequestCollection()
                    };
                    using (StreamReader srReadFile = new StreamReader(string.Format(strReadFilePath, (Convert.ToInt32(data) + 1).ToString("00"))))
                    {
                        while (!srReadFile.EndOfStream)
                        {
                            string strReadLine = srReadFile.ReadLine(); //读取每行数据
                            if (i != 0)//如果第一行包括标题的话要过滤掉
                            {
                                var arrLine = strReadLine.Split(',');
                                CreateRequest req = new CreateRequest();
                                var createEntity = new Entity("ly_test");
                                createEntity["ly_name"] = arrLine[0];
                                createEntity["ly_singletext1"] = arrLine[1];
                                createEntity["ly_singletext2"] = arrLine[2];
                                createEntity["ly_singletext3"] = arrLine[3];
                                createEntity["importsequencenumber"] = Convert.ToInt32(importsequencenumber);
                                req.Target = createEntity;
                                if (j <= 1000)
                                {
                                    multiReqs.Requests.Add(req);
                                }
                                else
                                {
                                    multiReqs.Requests = new OrganizationRequestCollection();
                                    multiReqs.Requests.Add(req);
                                    j = 1;
                                }
                                if (j == 1000)
                                {
                                    orgSvc.Execute(multiReqs);
                                    Console.WriteLine("线程:" + Thread.CurrentThread.ManagedThreadId + "-导入完毕" + z + "" + DateTime.Now.ToString());
                                }
                                z++;
                                j++;
                            }
                            i++;
                        }
                    }
                    orgSvc.Execute(multiReqs);
                    Console.WriteLine("线程结束" + DateTime.Now.ToLongTimeString() + ";线程ID:" + Thread.CurrentThread.ManagedThreadId);
                }
                catch(FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
                {
                    Console.WriteLine("执行遇到异常:" + ex.Detail.ErrorCode + ex.Message + ex.StackTrace);
                }
                catch (Exception e)
                {
                    Console.WriteLine("执行遇到异常:" + e.Message + e.StackTrace);
                }
            }
        }
    }

    这个程序配合的配置文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
      </startup>
      <appSettings>
        <add key="userName" value="crmadmin@luoyong.me"/>
        <add key="passWord" value="Pass"/>
        <add key="orgUrl" value="https://demo.luoyong.me/XRMServices/2011/Organization.svc"/>
        <add key="filename" value="D:dataimportdata{0}.csv"/>
        <add key="importsequencenumberstartat" value="1000000"/>
        <add key="threadcount" value="10"/>
      </appSettings>
    </configuration>
  • 相关阅读:
    libevent
    STL中mem_fun和mem_fun_ref的用法
    java的awt和swing的区别于联系
    数据库流程控制的使用IF CASE LOOP LEAVE ITERETA REPEAT WHILE
    mysql的常用函数
    数据库的基本知识点
    使用myeclipse 打包并运行普通java项目
    getClass()与getName()方法
    Java中的final关键字
    基本类型包装类的常量池技术
  • 原文地址:https://www.cnblogs.com/luoyong0201/p/Dynamics_365_CE_Import_Data.html
Copyright © 2011-2022 走看看