Code Copied

Dynamics CRM编程系列(1)获取IOrganizationService

摘要

Mazda的CRM项目做下来已经将近1年的时间了,并且仍然在持续,这个项目开始时我是对Dynamics CRM是一无所知,在这个项目中自己也在不断学习、探索和理解CRM。
现在已经对CRM有一定理解,由于时日尚短,并不敢造次地说对其理解深刻,在技术面前始终保持一颗虔诚学习的心态。
后面仍然需要多加实践,把CRM的常用技术领会,把业务真正融入技术中,让技术和业务互相配合起来,不仅给客户提供价值,并且提升自己的综合能力。
从技术角度讲,就我所接触到的一些CRM开发相关的内容,都离不开.NET和JavaScript,幸于我之前已经在.NET和JavaScript方面有5年多的经验,在业务层面也曾常常和客户打交道,微软在CRM SDK中提供了很多不错的Sample,所以在CRM开发层面上手较快。
在这里也提醒一下初入CRM这一领域的同仁:.NET的基础、JavaScript基础一定要扎实,这里的基础并不是指.NET或JavaScript的框架使用层面(例如.NET和JavaScript较多优秀的类库或框架),而是指JavaScript基础、CLR基础、设计模式、WCF原理等一些原理性的东西。

从本篇开始,我将逐步介绍一些CRM实战用到的一些功能点和代码,现在是这个系列的第一篇:获取IOrganizationService。

在文中提供的代码示例将同时适用于Dynamics CRM 2011, 2013和2015。
CRM SDK 2011下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=24004
CRM SDK 2013下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=40321
CRM SDK 2015下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=44567
MSDN教程地址:http://msdn.microsoft.com/en-us/library/gg328027.aspx

C#获取IOrganizationService

在C#中操作CRM数据的入口是IOrganizationService,例如操作Entity的Metadata和Record,都需要使用IOrganizationService的实例。
IOrganizationService有2个实现:OrganizationService类和OrganizationServiceProxy类。

SDK中的CrmServiceHelper

在CRM的SDK中提供了一个较为完善的连接类CrmServiceHelper.cs。
其路径为:SampleCode\CS\HelperCode\CrmServiceHelper.cs
这个class显得较为复杂,对于CRM初学者来说理解仍然会有一些困难,但如果你想直接使用这个class也是没有问题的。

我仔细阅读了CrmServiceHelper.cs的源码,这个class的功能可以总结为2点:
   
1.获取访问CRM的配置信息,配置信息包含OrganizationService地址、用户名、密码等等。
    2.获取OrganizationServiceProxy对象,该对象是IOrganizationService的一个实例。

这个近2000行代码的类仅做了这两件事,显得过于繁琐。
CRM Organization Service本质上是1个WCF服务,而OrganizationServiceProxy类是IOrganizationService的一个客户端代理类。
下图用WCF的方式解释了IOrganizationService和OrganizationServiceProxy的关系。

image

CrmServiceHelper提供了获取OrganizationServiceProxy对象的方法,但在MSDN上对该类进行描述时,推荐使用IOrganizationService的实例。

http://msdn.microsoft.com/zh-cn/library/microsoft.xrm.sdk.client.organizationserviceproxy.aspx
To call the Microsoft Dynamics CRM web service using the IOrganizationService interface, cast the proxy instance to an IOrganizationService type.

简单的CrmServiceHelper实现

创建工程

在Visual Studio中创建1个C#的Class Library工程:CrmLibrary。
再创建CrmLibrary对应的Test工程:CrmLibrary.Test。

image

CrmServiceHelper实现

在CrmLibrary中添加3个引用:Microsoft.Xrm.Client, Microsoft.Xrm.SDK, System.Runtime.Serialization

基于上述过程的描述,我编写了一个简易的CrmServiceHelper类:

using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;

namespace CrmLibrary
{
    public class CrmServiceHelper
    {

        #region 单例

        // 静态自动hold实例
        private static volatile CrmServiceHelper _instance;
        // Lock对象,线程安全用
        private static readonly object SyncRoot = new object();

        private CrmServiceHelper()
        {
        }

        public static CrmServiceHelper Instance
        {
            get
            {
                if (_instance != null) return _instance;
                lock (SyncRoot)
                {
                    _instance = new CrmServiceHelper();
                }
                return _instance;
            }
        }

        #endregion

        #region 字段

        private IOrganizationService _service;

        #endregion

        #region 属性

        /// <summary>
        /// CRM连接串
        /// </summary>
        public string ConnectionString { get; set; }

        #endregion

        #region 公共方法

        /// <summary>
        /// 获取IOrganizationService实例
        /// </summary>
        /// <returns></returns>
        public IOrganizationService GetService()
        {
            if (_service == null)
            {
                var connection = CrmConnection.Parse(ConnectionString);
                _service = new OrganizationService(connection);
            }
            return _service;
        }

        #endregion 
    }
}

最关键的2行代码已经用黄底标出来了:

  1. 根据Crm连接串创建CrmConnection对象。
  2. 根据CrmConnection对象创建IOrganizationService的实例。

ConnectionString的格式如下:

"Url=http://192.168.137.138:5555/CrmOrg/XRMServices/2011/Organization.svc; Username=mscrm\\administrator;Password=123456;";

ConnectionString有3个组成部分:

  1. Url:OrganizationService的Url地址
  2. Username:访问CRM的用户名
  3. Password:访问CRM的密码

看到这里,是否觉得这种实现风格非常类似于ADO.NET的DB连接,是否更容易理解呢?

CrmServiceHelper单元测试

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

namespace CrmLibrary.Test
{
    [TestClass]
    public class CrmServiceHelperTests
    {
        [TestMethod]
        public void GetServiceTest()
        {
            var crmServiceHelper = CrmServiceHelper.Instance;
            crmServiceHelper.ConnectionString = "Url=http://192.168.137.138:5555/CrmOrg/XRMServices/2011/Organization.svc; Username=mscrm\\administrator;Password=123456;";
            IOrganizationService service = crmServiceHelper.GetService();

            // 验证service是否为空
            Assert.IsNotNull(service);

            var request = new RetrieveEntityRequest
            {
                EntityFilters = EntityFilters.All,
                LogicalName = "account"
            };
            var response = (RetrieveEntityResponse)service.Execute(request);

            // 验证service对象是否能够成功执行请求
            Assert.IsNotNull(response);
        }
    }
}

这个单元测试中做了2个验证:

  1. 验证IOrganizationService对象是否为空
  2. 验证IOrganizationService是否能够成功执行请求

验证结果:

image

CrmServiceHelper重构

虽然上面已经实现了获取IOrganizationService的方法,但是CrmServiceHelper仍然存在2个问题:

  1. 每次调用GetService()方法之前都需要先获取CrmServiceHelper的实例,然后必须提供ConnectionString属性。
  2. GetService()方法过于单一,只能用无参数方式调用,不够灵活。

为了解决这2个问题,我对CrmServiceHelper进行了重构:

using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;

namespace CrmLibrary
{
    public class CrmServiceHelper
    {

        #region 单例

        // 静态自动hold实例
        private static volatile CrmServiceHelper _instance;
        // Lock对象,线程安全用
        private static readonly object SyncRoot = new object();

        private CrmServiceHelper()
        {
        }

        public static CrmServiceHelper Instance
        {
            get
            {
                if (_instance != null) return _instance;
                lock (SyncRoot)
                {
                    _instance = new CrmServiceHelper();
                }
                return _instance;
            }
        }

        #endregion

        #region 字段

        private IOrganizationService _service;
        private string _connectionString = "Url=http://192.168.137.138:5555/CrmOrg/XRMServices/2011/Organization.svc; Username=mscrm\\administrator;Password=sunny123$;";

        #endregion

        #region 属性



        /// <summary>
        /// CRM连接串
        /// </summary>
        public string ConnectionString
        {
            get { return _connectionString; }
            set { _connectionString = value; }
        }

        #endregion

        #region 公共方法

        /// <summary>
        /// 获取IOrganizationService实例
        /// </summary>
        /// <returns></returns>
        public IOrganizationService GetService()
        {
            if (_service == null)
            {
                var connection = CrmConnection.Parse(ConnectionString);
                _service = new OrganizationService(connection);
            }
            return _service;
        }

        /// <summary>
        /// 获取IOrganizationService实例(根据CRM连接串)
        /// </summary>
        /// <param name="connectionString">CRM连接串</param>
        /// <returns></returns>
        public IOrganizationService GetService(string connectionString)
        {
            var connection = CrmConnection.Parse(connectionString);
            _service = new OrganizationService(connection);
            return _service;
        }

        #endregion 
    }
}

现在这个class可以满足以下几种调用方式:

  1. CrmServiceHelper作为内部调用时(代码可见),直接使用CrmServiceHelper本身提供的_connectionString字段作为连接串,这时应该调用GetService()方法。
  2. CrmServiceHelper作为外部调用时(作为dll调用),应该调用GetService(string connectionString)方法。