关注本人微信和易信公众号: 微软动态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);