一種提高鏈接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中使用組織服務的話,通常都是每次查詢會創建一次鏈接,代碼相似以下:ide

 

複製代碼

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秒鐘。wordpress

 

我用fiddler抓包,你會發現每次認證都會下載元數據,一共下載了5次。網站

 

若是我改動代碼以下,類 ManagedTokenOrganizationServiceProxy 代碼來自Ali Sharifi 的文章 REFRESH SECURITY TOKEN FOR MICROSOFT DYNAMICS CRM CONNECTION ,可是我發現沒什麼用,由於 this._proxy.SecurityTokenResponse == null,因此我這裏不會使用。ui

複製代碼

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秒,速度提高很多。this

 

咱們看看Fiddler抓包效果,僅僅只有一次下載元數據,並且由於緩存執行的很是快。spa

 

若是要緩存的話,我這裏在Application Start事件時候緩存代碼以下,固然要添加對 System.Runtime.Caching 的引用。線程

複製代碼

using System.Runtime.Caching;            
ObjectCache cache == =<IOrganizationService> sm = ServiceConfigurationFactory.CreateManagement<IOrganizationService>( Uri(ConfigurationManager.AppSettings[= = ConfigurationManager.AppSettings[= ConfigurationManager.AppSettings[, sm.Authenticate(authCredentials), policy);

複製代碼

 

而後在代碼中獲取組織服務就用以下代碼:翻譯

using System.Runtime.Caching;
ObjectCache cache ==  OrganizationServiceProxy(((IServiceManagement<IOrganizationService>)cache[]), ((AuthenticationCredentials)cache[]).ClientCredentials);
相關文章
相關標籤/搜索