Dynamics CRM 2011编程系列(54):MagicBox实现原理

      文章《Dynamics CRM 2011编程系列(53):客户端实体序列化工具--MagicBox 》分享了一款序列化实体为JSON字符串的工具,该文章仅仅介绍了如何使用它来生成JSON字符串并没有介绍它的实现原理。本文就来描述下该工具的设计思路以及实现原理吧。

    在Dynamics CRM 2011系统中,如果我们想在客户端对记录进行CRUD操作,就现在来说有两种方式:其一为调用REST端点,另一为用JS构造与CRUD对应SOAP报文并发送给服务端。这两种方法我之前都介绍过,今天我们要谈的是第一种方法。

    Dynamics CRM 2011中的REST端点是ROA概念的解决方案,ROA即为面向资源。ROA指出互联网上的所有资源都可以用一种格式来进行描述,无论是获取它还是对它进行修改,这种格式就是我们都熟悉的---URI,因此OData就此诞生了。我们看几个OData查询URL吧,这些操作中除Read操作是以GET方式请求请求服务器,其他的均为POST方式。MagicBox生成的JSON字符串主要用在Create和Update操作上。:

Create
var path="/GH2011/XRMServices/2011/OrganizationData.svc/ContactSet"; 

Read
var query="/GH2011/XRMServices/2011/OrganizationData.svc/AccountSet?$top=5&$orderby=CreatedOn desc";  

Update(需要设置请求报头 X-HTTP-Method的值为MERGE)
var uptUrl="/GH2011/XRMServices/2011/OrganizationData.svc/OpportunitySet(guid'{366BE04B-47D9-E011-9C38-000C2956A039}')";   

Delete(需要设置请求报头 X-HTTP-Method的值为DELETE)
var delUrl="/GH2011/XRMServices/2011/OrganizationData.svc/ContactSet(guid'{366BE04B-47D9-E011-9C38-000C2956A039}')";     

 

MagicBox的Callflow顺序

Click GO button--〉GenerateEntity()--> GenerateSerializationString()

                                                   --> GetAttributeMetaData()

                                                                                                                                                                                                                  -->GetEntityMetadata()

    MagicBox中的方法GetEntityMetadata()会根据我们提供的实体名称获取相应的实体元数据,我们在这些元数据中提取我们需要的属性元数据。因为REST端点接受Create/Update提交的JSON字符串是区分大小写的,它是根据属性的SchemaName来进行匹配的。在我们开发的时候我们并不能保证所有的实体的SchemaName都是小写或都是大写,所以很容易出现拼写错误,因此我们很有必要把这个容易出错的环节自动化起来.。

获取实体元数据(只获取当前实体的属性类型的元数据)

        private EntityMetadata GetEntityMetadata(string entityName)
        {
            if(EntityMetaDataTable.ContainsKey(entityName)==false)
            {
                IOrganizationService svc = CommonLibrary.CrmSvcHelper.CreateService();
                RetrieveEntityRequest request = new RetrieveEntityRequest();
                request.EntityFilters = EntityFilters.Attributes;
                request.LogicalName = entityName;

                RetrieveEntityResponse response = svc.Execute(request) as RetrieveEntityResponse;
                if (response != null && response.EntityMetadata != null)
                {
                    EntityMetaDataTable.Add(entityName, response.EntityMetadata);
                }
                else
                {
                    return null;
                }
            }

            return EntityMetaDataTable[entityName];

            
            
        }


    光有实体的元数据还是不行的,现在我们还缺少一个对应关系:服务端实体的属性结构和客户端JSON字符串的对应关系。我们可以通过Fiddler抓包来获取这个关系,看官们有兴趣可以自己尝试下,如下是我抓取的对应关系:

{
 'ms_currencytype':{'Value':'1.0000'}  --Money Type
,'StateCode':{'Value':'0'}  --State Type
,'ms_stringtype':'string type'  --string type
,'ms_optionsettype':{'Value':'2'}  --picklist type
,'StatusCode':{'Value':'1'}  --state reason type
,'ms_floattype':'12.3'  --float type
,'OwnerId':{'LogicalName':'systemuser','Id':'f703d401-5a01-e211-90cd-00155db7bf1b'}  --owner type
,'ms_decimaltype':'12.5000000000'  --decimal type
,'ms_booleantype':'True'  --boolean type
,'ms_datetimetype':'4/17/2013 2:34:14 AM'  --datetime type
,'ms_lookup':{'LogicalName':'account','Id':'f88a7ff6-74a6-e211-b2be-00155d1ce815'}  --lookup type
,'ms_numbertype':'123'  --number type
}


既然有了这个关系,咱们就可以制作一个解析器了:

private string GenerateSerializationString(Entity entity)
        {
            StringBuilder result = new StringBuilder();
            string tmp=string.Empty;
            Microsoft.Xrm.Sdk.AttributeCollection attributes = entity.Attributes;
            AttributeMetadata attrMetadata=null;
            result.Append("{");
            foreach (var item in attributes)
            {
                attrMetadata=GetAttributeMetadata(entity.LogicalName,item.Key);
                switch (attrMetadata.AttributeType.Value)
                {
                    case AttributeTypeCode.BigInt:
                        result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Boolean:
                        result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.CalendarRules:
                        break;
                    case AttributeTypeCode.Customer:

                        break;
                    case AttributeTypeCode.DateTime:
                        result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Decimal:
                        result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Double:
                        result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.EntityName:
                        break;
                    case AttributeTypeCode.Integer:
                         result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Lookup:
                        result.Append(string.Format("'{0}':{{'LogicalName':'{1}','Id':'{2}'}}", attrMetadata.SchemaName, ((EntityReference)item.Value).LogicalName, ((EntityReference)item.Value).Id));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.ManagedProperty:
                        break;
                    case AttributeTypeCode.Memo:
                        break;
                    case AttributeTypeCode.Money:
                        result.Append(string.Format("'{0}':{{'Value':'{1}'}}", attrMetadata.SchemaName, ((Money)item.Value).Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Owner:
                        result.Append(string.Format("'{0}':{{'LogicalName':'{1}','Id':'{2}'}}", attrMetadata.SchemaName, ((EntityReference)item.Value).LogicalName, ((EntityReference)item.Value).Id));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.PartyList:
                        break;
                    case AttributeTypeCode.Picklist:
                        result.Append(string.Format("'{0}':{{'Value':'{1}'}}", attrMetadata.SchemaName, ((OptionSetValue)item.Value).Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.State:
                        result.Append(string.Format("'{0}':{{'Value':'{1}'}}", attrMetadata.SchemaName, ((OptionSetValue)item.Value).Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Status:
                        result.Append(string.Format("'{0}':{{'Value':'{1}'}}", attrMetadata.SchemaName, ((OptionSetValue)item.Value).Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.String:
                        result.Append(string.Format("'{0}':'{1}'",attrMetadata.SchemaName,item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Uniqueidentifier:
                        result.Append(string.Format("'{0}':'{1}'", attrMetadata.SchemaName, item.Value));
                        result.Append(",");
                        break;
                    case AttributeTypeCode.Virtual:
                        break;
                    default:
                        break;
                }
            }
            
            tmp=result.ToString();
            return tmp.Substring(0, tmp.Length - 1) + "}";// remove the last ,
        }

 

   现在JSON字符串的处理工作完成了,但我们需要向这个处理器进行相应的输入。在这个版本的MagicBox中,我没打算把它实现的很友好,毕竟它只是给咱们开发人使用的。所以就以填充代码的方式来传递需要解析的实体记录了:

        private Entity GenerateEntity()
        {
            //Type your codes to retrieve a entity which you want to use.
            //Start

            IOrganizationService svc = CommonLibrary.CrmSvcHelper.CreateService();
            Guid entityId = Guid.Parse("{204B9D72-20A7-E211-B2BE-00155D1CE815}");
            return svc.Retrieve("ms_fighter", entityId, new Microsoft.Xrm.Sdk.Query.ColumnSet(true));

            //End
        }


 

小结

    在方法GenerateSerializationString()中有几个属性是没有被处理的。因为我没有找到客户描述它们的JSON影射关系,各位如果知道它们的映射关系可以联系我。


 

 

 

 

 

 

你可能感兴趣的:(Dynamics CRM 2011编程系列(54):MagicBox实现原理)