知识为进步之母,而进步又为富强之源泉。这篇文章主要讲述使用程序生成要部署的实体和目标环境实体的差别相关的知识,希望能为你提供帮助。
我是微软Dynamics 365 &
Power Platform方面的工程师/顾问罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP),欢迎关注我的微信公众号 MSFTDynamics365erLuoYong ,回复466或者20220313可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!
我们发布解决方案到目标环境,希望发布的内容尽量精准,发布过去的组件尽量只是需要发布的,不要带其他不需要发布的组件,如果要检查呢?一个个手工比较太Low。我目前没有找到很好的办法,根据项目的实践,一般容易造成问题的主要是实体以及它的组件,我就准备了一个程序,读取导出解决方案的
customizations.xml
内容和要导入的目标环境的实体元数据进行对比,然后将对比结果生成Excel方便分析和比较。
关于查询实体元数据可以参考我前面的博文:
- ??Dynamics CRM使用元数据之一:查询实体的主字段(托管代码版本) ??
- ??Dynamics CRM 2015/2016新特性之二十七:使用Web API查询元数据??
- ??Dynamics 365使用JavaScript调用Web API批量设置字段的审核属性为禁用??
using Microsoft.Xrm.Tooling.Connector;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Office.Interop.Excel;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using System.IO;
using Microsoft.Xrm.Sdk.Query;
namespace CompareComponents
class Program
public static string[] excludeColumns = "ownerid,owningbusinessunit,owningteam,owninguser,statecode,modifiedby,modifiedbyexternalparty,modifiedon,modifiedonbehalfby,overriddencreatedon,createdby,createdbyexternalparty,createdon,createdonbehalfby,entityimage".Split(,);
static void Main(string[] args)
Console.OutputEncoding = Encoding.GetEncoding("gb2312");
Console.WriteLine("本程序用于读取解决方案中的customizations.xml中内容和要布署的目标环境组件进行对比!,开始使用请输入Y!");
var inputValue = https://www.songbingjia.com/android/Console.ReadLine();
if (inputValue.Equals("Y", StringComparison.OrdinalIgnoreCase))
Console.WriteLine("开始读取customizations.xml文件中内容!");
var customizationsFilePath = ConfigurationManager.AppSettings["CustomizationsFilePath"];
List< TableMetadata> lsTables = new List< TableMetadata> ();
XElement componentstoDeploy = XElement.Load(customizationsFilePath);
IEnumerable< XElement> tableEles = from item in componentstoDeploy.Descendants("Entities").FirstOrDefault().Descendants("Entity")
select item;
foreach(var tableEle in tableEles)
var table = new TableMetadata();
table.TableDispalyName = tableEle.Element("Name").Attribute("LocalizedName").Value.ToString();
table.TableSchemaName = tableEle.Element("Name").Value.ToString();
table.Columns = new List< ColumnMetadata> ();
table.Forms = new List< FormMetadata> ();
table.SavedQueries = new List< SavedQueryMetadata> ();
table.Visualizations = new List< VisualizationMetadata> ();
//处理列
if (tableEle.Descendants("attributes").Any())
var columnEles = from item in tableEle.Descendants("attributes").FirstOrDefault().Descendants("attribute")
select item;
foreach (var columnEle in columnEles)
var columnLogicalName = columnEle.Attribute("PhysicalName").Value.ToString().ToLower();
var columnDataType = columnEle.Element("Type").Value.ToString();
if (!excludeColumns.Contains(columnLogicalName) & & !columnDataType.Equals("primarykey", StringComparison.OrdinalIgnoreCase))
table.Columns.Add(new ColumnMetadata()
LogicalName = columnLogicalName,
DataType = ChangeColumnType(columnDataType)
);
//处理表单
if (tableEle.Element("FormXml") != null)
var formEles = from item in tableEle.Element("FormXml").Descendants("forms")
select item;
foreach (var formEle in formEles)
foreach (var systemfrom in formEle.Descendants("systemform"))
table.Forms.Add(new FormMetadata()
Name = systemfrom.Element("LocalizedNames").Descendants("LocalizedName").FirstOrDefault().Attribute("description").Value.ToString(),
FormId = Guid.Parse(systemfrom.Element("formid").Value.ToString())
);
//处理公共视图
if (tableEle.Element("SavedQueries") != null)
var savedqueryEles = from item in tableEle.Element("SavedQueries").推荐阅读
- MySQL开发篇,存储引擎选择真的很重要吗()
- Redis入门到实践,由持久化引发的思考
- VMware Tools 启动脚本未能在虚拟机中成功运行。如果您在此虚拟机中配置了自定义启动脚本,请确保该脚本没有错误。您也可以提交支持请求,报告此问题。
- #yyds干货盘点# Redis键过期策略详解
- 虚拟机的安装与使用
- 第五篇- 抖音的强大对手来了,用Flutter开发一个抖音国际版,看看有多炫
- 虚拟机的安装和使用
- 图解数据结构树和二叉树全面总结
- 字符函数和字符串函数