• 一种提升连接Dynamics 365性能的方法


    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复256或者20170512可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me 。
     
    最近做代码审查,有个建议是Improve Microsoft Dynamics CRM service channel allocation performance的建议,请教了微软专家,同事也参考了官方的文章:Best practices for developing with Dynamics 365 for Customer Engagement ,Get了一下新技能,下面我做一次传声筒和翻译器,请听我讲来。
    为了更好的看到效果,我这里使用了多个线程来凸显效果。我以前的项目,不在Dynamics 365中使用组织服务的话,一般都是每次查询会建立一次连接,代码类似如下:
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Query;
    using Microsoft.Xrm.Tooling.Connector;
    using System;
    using System.ServiceModel;
    using System.Threading;
    
    namespace LuoYongLab
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    for (var i = 0; i < 5; i++)
                    {
                        ThreadStart tStart = new ThreadStart(Work);
                        Thread thread = new Thread(tStart);
                        thread.Start();
                    }
                    Console.WriteLine("程序运行完成!");
                    Console.ReadKey();
                }
                catch (FaultException ex)
                {
                    Console.WriteLine("程序出现异常:ex.Message=" + ex.Message);
                    Console.ReadKey();
                }
            }
    
            static void Work()
            {
                Console.WriteLine("线程开始" + DateTime.Now.ToLongTimeString() + ";线程ID:" + Thread.CurrentThread.ManagedThreadId);
                var crmSvc = new CrmServiceClient(new System.Net.NetworkCredential("crmadmin@luoyong.me", "Pass", null), Microsoft.Xrm.Tooling.Connector.AuthenticationType.IFD, "demo.luoyong.me", "443", "demo", useUniqueInstance: false, useSsl: true);
                Console.WriteLine("线程ID: " + Thread.CurrentThread.ManagedThreadId + ";Token过期时间:" + crmSvc.OrganizationServiceProxy.SecurityTokenResponse.Response.Lifetime.Expires);
                if (crmSvc.IsReady)
                {
                    QueryExpression qe = new QueryExpression("organization");
                    qe.ColumnSet = new ColumnSet("languagecode", "basecurrencyid");
                    EntityCollection ec = crmSvc.RetrieveMultiple(qe);
                    if (ec.Entities.Count >= 1)
                    {
                        Console.WriteLine("线程ID: " + Thread.CurrentThread.ManagedThreadId + ";组织偏好语言:" + ec.Entities[0].GetAttributeValue<int>("languagecode"));
                    }
                }
                Console.WriteLine("线程结束" + DateTime.Now.ToLongTimeString() + ";线程ID:" + Thread.CurrentThread.ManagedThreadId);
            }
        }
    }
    效果如下,可以看到大概需要7秒钟。
     
    我用fiddler抓包,你会发现每次认证都会下载元数据,一共下载了5次。
     
    如果我改动代码如下,类 ManagedTokenOrganizationServiceProxy 代码来自Ali Sharifi 的文章 REFRESH SECURITY TOKEN FOR MICROSOFT DYNAMICS CRM CONNECTION ,但是我发现没什么用,因为 this._proxy.SecurityTokenResponse == null,所以我这里不会使用。
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using Microsoft.Xrm.Sdk.Query;
    using System;
    using System.Configuration;
    using System.ServiceModel;
    using System.Threading;
    
    namespace ConsoleApp
    {
        class Program
        {
            public static IServiceManagement<IOrganizationService> sm;
            public static AuthenticationCredentials authCredentials;
            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 < 5; i++)
                    {
                        ThreadStart tStart = new ThreadStart(Work);
                        Thread thread = new Thread(tStart);
                        thread.Start();
                    }
                    Console.WriteLine("程序运行完成!");
                    Console.ReadKey();
                }
                catch (FaultException ex)
                {
                    Console.WriteLine("程序出现异常:ex.Message=" + ex.Message);
                    Console.ReadKey();
                }
            }
    
            static void Work()
            {
                Console.WriteLine("线程开始" + DateTime.Now.ToLongTimeString() + ";线程ID:" + Thread.CurrentThread.ManagedThreadId);
                OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(sm, authCredentials.ClientCredentials);
                //OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(sm, authCredentials.SecurityTokenResponse);
                //ManagedTokenOrganizationServiceProxy orgSvc = new ManagedTokenOrganizationServiceProxy(sm, authCredentials.ClientCredentials);
                QueryExpression qe = new QueryExpression("organization");
                qe.ColumnSet = new ColumnSet("languagecode", "basecurrencyid");
                EntityCollection ec = orgSvc.RetrieveMultiple(qe);
                if (ec.Entities.Count >= 1)
                {
                    Console.WriteLine("线程ID: " + Thread.CurrentThread.ManagedThreadId + ";组织偏好语言:" + ec.Entities[0].GetAttributeValue<int>("languagecode"));
                }
                Console.WriteLine("线程结束" + DateTime.Now.ToLongTimeString() + ";线程ID:" + Thread.CurrentThread.ManagedThreadId);
            }
        }
    }
     
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    
    namespace ConsoleApp
    {
    
        public sealed class ManagedTokenOrganizationServiceProxy : OrganizationServiceProxy
        {
            private AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService> _proxyManager;
    
            public ManagedTokenOrganizationServiceProxy(Uri serviceUri, ClientCredentials userCredentials)
                : base(serviceUri, null, userCredentials, null)
            {
                this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
            }
    
            public ManagedTokenOrganizationServiceProxy(IServiceManagement<IOrganizationService> serviceManagement,
                SecurityTokenResponse securityTokenRes)
                : base(serviceManagement, securityTokenRes)
            {
                this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
            }
    
            public ManagedTokenOrganizationServiceProxy(IServiceManagement<IOrganizationService> serviceManagement,
                ClientCredentials userCredentials)
                : base(serviceManagement, userCredentials)
            {
                this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
            }
    
            protected override void AuthenticateCore()
            {
                this._proxyManager.PrepareCredentials();
                base.AuthenticateCore();
            }
    
            protected override void ValidateAuthentication()
            {
                this._proxyManager.RenewTokenIfRequired();
                base.ValidateAuthentication();
            }
        }
    
        ///<summary>
        /// Class that wraps acquiring the security token for a service
        /// </summary>
    
        public sealed class AutoRefreshSecurityToken<TProxy, TService>
            where TProxy : ServiceProxy<TService>
            where TService : class
        {
            private TProxy _proxy;
    
            ///<summary>
            /// Instantiates an instance of the proxy class
            /// </summary>
    
            /// <param name="proxy">Proxy that will be used to authenticate the user</param>
            public AutoRefreshSecurityToken(TProxy proxy)
            {
                if (null == proxy)
                {
                    throw new ArgumentNullException("proxy");
                }
    
                this._proxy = proxy;
            }
    
            ///<summary>
            /// Prepares authentication before authenticated
            /// </summary>
    
            public void PrepareCredentials()
            {
                if (null == this._proxy.ClientCredentials)
                {
                    return;
                }
    
                switch (this._proxy.ServiceConfiguration.AuthenticationType)
                {
                    case AuthenticationProviderType.ActiveDirectory:
                        this._proxy.ClientCredentials.UserName.UserName = null;
                        this._proxy.ClientCredentials.UserName.Password = null;
                        break;
                    case AuthenticationProviderType.Federation:
                    case AuthenticationProviderType.LiveId:
                        this._proxy.ClientCredentials.Windows.ClientCredential = null;
                        break;
                    default:
                        return;
                }
            }
    
            ///<summary>
            /// Renews the token (if it is near expiration or has expired)
            /// </summary>
    
            public void RenewTokenIfRequired()
            {
                if (null != this._proxy.SecurityTokenResponse &&
                DateTime.UtcNow.AddMinutes(15) >= this._proxy.SecurityTokenResponse.Response.Lifetime.Expires)
                {
                    try
                    {
                        this._proxy.Authenticate();
                    }
                    catch (CommunicationException)
                    {
                        if (null == this._proxy.SecurityTokenResponse ||
                            DateTime.UtcNow >= this._proxy.SecurityTokenResponse.Response.Lifetime.Expires)
                        {
                            throw;
                        }
                    }
                }
            }
        }
    }
    我们可以看到执行时间也会缩短,从之前的大概需要7秒降低到需要1秒,速度提升不少。
     
    我们看看Fiddler抓包效果,仅仅只有一次下载元数据,而且因为缓存执行的非常快。
     
    如果要缓存的话,我这里在Application Start事件时候缓存代码如下,当然要添加对 System.Runtime.Caching 的引用。
    using System.Runtime.Caching;            
    ObjectCache cache = MemoryCache.Default; CacheItemPolicy policy = new CacheItemPolicy(); policy.Priority = CacheItemPriority.NotRemovable; IServiceManagement<IOrganizationService> sm = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(ConfigurationManager.AppSettings["orgUrl"])); cache.Set("sm", sm, policy); AuthenticationCredentials authCredentials = new AuthenticationCredentials(); authCredentials.ClientCredentials.UserName.UserName = ConfigurationManager.AppSettings["userName"]; authCredentials.ClientCredentials.UserName.Password = ConfigurationManager.AppSettings["passWord"]; cache.Set("authCredentials", sm.Authenticate(authCredentials), policy);

    然后在代码中获取组织服务就用如下代码:

    using System.Runtime.Caching;
    ObjectCache cache = MemoryCache.Default; OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(((IServiceManagement<IOrganizationService>)cache["sm"]), ((AuthenticationCredentials)cache["authCredentials"]).ClientCredentials);
     
  • 相关阅读:
    InfoQ访谈BPEL4People代表
    传 IBM 拟 4 月 6 日宣布收购 Sun
    NetBeans 6.7 Milestone 3 Now Available for Download!
    Intel比AMD高明在哪里?
    InfoQ访谈BPEL4People代表
    Linux 3.8.1 电源管理之OMAP Voltage Domain分析
    Readline简介 Linux技术问答 Linux中国 | Linux.cn 我们的Linux中文社区
    更改日期
    JAVA研发工程师(YF)
    一键解决Ubuntu下安装Eclipse Android/C/C++ 开发环境
  • 原文地址:https://www.cnblogs.com/luoyong0201/p/Dynamics_365_High_Performance_Connection_Method.html
Copyright © 2020-2023  润新知