关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复239或者20161203可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me 。

以前我出过一个悬赏题目,就是定制Dynamics CRM标准的导出功能,让某些列不能导出,比如这些列可能包括敏感信息,比如消费者的姓名和电话等等,如何定制呢?我到现在还没有收到答案。某个周末的晚上,我研究了一下,初步有些效果,所以本博文记下来。
最容易想到的是注册插件,与导出有关的消息至少有四个,分别是Export, ExportAll, ExportCompressed 和ExportCompressedAll,可是注册在这四个消息上不管用。于是想到了在RetrieveMultiple消息上动刀,这个消息经常使用,除了根据主键和备用键查询数据以外,应该都是通过这个消息查询数据,导出也要查询数据。可是如果每次查询数据都执行的话,很消耗性能,后来我发现导出的时候执行的这个消息和普通的查询似乎有一个区别,就是导出的时候执行这个消息的上下文的Depth属性值为2,所以可以利用这个属性做点文章,尽量少执行点代码。
我先使用如下代码来查看打开一个普通的视图,插件中获取的各种参数,我这个代码是注册在罗勇测试实体RetrieveMultiple的Pre阶段。
using System;
using Microsoft.Xrm.Sdk;
using System.Text;
using Microsoft.Xrm.Sdk.Query;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO; namespace CrmVSSolution.Plugins
{
public class PreTestRetrieveMultiple : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// If you are not registering the plug-in in the sandbox, then you do
// not have to add any tracing service related code.
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService)); // Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); StringBuilder sb = new StringBuilder();
sb.Append("Depth=");
sb.Append(context.Depth);
sb.Append(";PrimaryEntityName=");
sb.Append(context.PrimaryEntityName);
sb.Append(";PrimaryEntityId=");
sb.Append(context.PrimaryEntityId);
sb.Append(";BusinessUnitId=");
sb.Append(context.BusinessUnitId);
sb.Append(";InitiatingUserId=");
sb.Append(context.InitiatingUserId);
sb.Append(";IsExecutingOffline");
sb.Append(context.IsExecutingOffline);
sb.Append(";IsInTransaction=");
sb.Append(context.IsInTransaction);
sb.Append(";IsOfflinePlayback=");
sb.Append(context.IsOfflinePlayback);
sb.Append(";IsolationMode=");
sb.Append(context.IsolationMode);
sb.Append(";MessageName=");
sb.Append(context.MessageName);
sb.Append(";OperationId=");
sb.Append(context.OperationId);
sb.Append(";OrganizationId=");
sb.Append(context.OrganizationId);
sb.Append(";OrganizationName=");
sb.Append(context.OrganizationName);
sb.Append(";OutputParameters=");
if (context.OutputParameters != null)
{
sb.Append("{");
foreach (var outputpara in context.OutputParameters)
{
sb.Append(outputpara.Key);
sb.Append("=");
sb.Append(outputpara.Value.ToString());
}
sb.Append("}");
}
else
{
sb.Append("null");
}
sb.Append(";OwningExtension=");
if (context.OwningExtension != null)
{
sb.Append("{LogicalName=");
sb.Append(context.OwningExtension.LogicalName);
sb.Append(";Name=");
sb.Append(context.OwningExtension.Name);
sb.Append(";Id=");
sb.Append(context.OwningExtension.Id);
}
else
{
sb.Append("null");
}
sb.Append(";ParentContext=");
if (context.ParentContext != null)
{
sb.Append(context.ParentContext.ToString());
}
else
{
sb.Append("null");
}
sb.Append(";PostEntityImages=");
if (context.PostEntityImages != null)
{
sb.Append("{");
foreach (var postimg in context.PostEntityImages)
{
sb.Append("Key=");
sb.Append(postimg.Key);
sb.Append(";Value.Id=");
sb.Append(postimg.Value.Id);
sb.Append(";Value.LogicalName=");
sb.Append(postimg.Value.LogicalName);
foreach (var attr in postimg.Value.Attributes)
{
sb.Append("Attr=");
sb.Append(attr.Key);
sb.Append(";Value=");
sb.Append(attr.Value.ToString());
}
}
sb.Append("}");
}
else
{
sb.Append("null");
}
sb.Append(";PreEntityImages=");
if (context.PreEntityImages != null)
{
sb.Append("{");
foreach (var preimg in context.PreEntityImages)
{
sb.Append("Key=");
sb.Append(preimg.Key);
sb.Append(";Value.Id=");
sb.Append(preimg.Value.Id);
sb.Append(";Value.LogicalName=");
sb.Append(preimg.Value.LogicalName);
foreach (var attr in preimg.Value.Attributes)
{
sb.Append("Attr=");
sb.Append(attr.Key);
sb.Append(";Value=");
sb.Append(attr.Value.ToString());
}
}
sb.Append("}");
}
else
{
sb.Append("null");
}
sb.Append(";SecondaryEntityName=");
sb.Append(context.SecondaryEntityName);
sb.Append(";RequestId=");
sb.Append(context.RequestId.HasValue ? context.RequestId.Value.ToString() : "null");
sb.Append(";Stage=");
sb.Append(context.Stage);
sb.Append(";UserId=");
sb.Append(context.UserId);
sb.Append(";InputParameters=");
if (context.InputParameters != null)
{
sb.Append("{");
foreach (var inputpara in context.InputParameters)
{
sb.Append(inputpara.Key);
sb.Append("=");
sb.Append(inputpara.Value.ToString());
if (inputpara.Key == "Query")
{
var queryexp = inputpara.Value as QueryExpression;
sb.Append("ColumnSet={");
foreach (var col in queryexp.ColumnSet.Columns)
{
sb.Append(col);
sb.Append(",");
}
sb.Append("}");
sb.Append("LinkEntities={");
if (queryexp.LinkEntities != null)
{
sb.Append("LinkEntity={");
foreach (var linkentity in queryexp.LinkEntities.Where(x => x.LinkToEntityName == "ly_test"))
{
sb.Append("LinkToEntityName=");
sb.Append(linkentity.LinkToEntityName);
sb.Append(";LinkFromEntityName=");
sb.Append(linkentity.LinkFromEntityName);
sb.Append(";LinkColumnSet={");
sb.Append(string.Join(",", linkentity.Columns.Columns));
sb.Append("}");
}
sb.Append("}");
}
else
{
sb.Append("null");
}
sb.Append("}");
}
}
sb.Append("}");
}
else
{
sb.Append("null");
}
sb.Append("当前用户拥有角色:");
sb.Append(string.Join(",", GetUserRoles(service, context.UserId)));
if (context.InputParameters.Contains("Query") && context.InputParameters["Query"] is QueryExpression)
{
//throw new InvalidPluginExecutionException("不是管理员不能导出!");//这个错误提示不会展示在前台给用户看到
QueryExpression objQueryExpression = (QueryExpression)context.InputParameters["Query"];
sb.Append("查找的主实体是:");
sb.Append(objQueryExpression.EntityName);
sb.Append("查询表达式是:");
XmlSerializer xmlSerializer = new XmlSerializer(objQueryExpression.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, objQueryExpression);
sb.Append(textWriter.ToString());
}
}
var entity = new Entity("annotation");
entity["subject"] = "execute retrievemultiple";
entity["objectid"] = new EntityReference("ly_test", Guid.Parse("B707DE1B-CF99-E611-8161-000D3A80C8B8"));
entity["notetext"] = sb.ToString();
service.Create(entity);
} public IEnumerable<string> GetUserRoles(IOrganizationService service, Guid userId)
{
string fetchXml = string.Format(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true' no-lock='true'>
<entity name='role'>
<attribute name='name' />
<link-entity name='systemuserroles' from='roleid' to='roleid' visible='false' intersect='true'>
<link-entity name='systemuser' from='systemuserid' to='systemuserid' alias='ad'>
<filter type='and'>
<condition attribute='systemuserid' operator='eq' value='{0}' />
</filter>
</link-entity>
</link-entity>
</entity>
</fetch>", userId);
EntityCollection entities = service.RetrieveMultiple(new FetchExpression(fetchXml));
return entities.Entities.Select(x => x.GetAttributeValue<string>("name"));
}
}
}
 
得到的结果如下:
Depth=1;PrimaryEntityName=ly_test;PrimaryEntityId=00000000-0000-0000-0000-000000000000;BusinessUnitId=487cdd4b-26a3-e511-80c6-000d3a807ec7;InitiatingUserId=e9cd027f-26a3-e511-80c6-000d3a807ec7;IsExecutingOffline:False;IsInTransaction=False;IsOfflinePlayback=False;IsolationMode=2;MessageName=RetrieveMultiple;OperationId=00000000-0000-0000-0000-000000000000;OrganizationId=bd2a5c49-6b08-4eda-8a15-84159d9fd349;OrganizationName=Demo;OutputParameters={};OwningExtension={LogicalName=sdkmessageprocessingstep;Name=CrmVSSolution.Plugins.PreTestRetrieveMultiple: RetrieveMultiple of ly_test;Id=544ba42f-baae-e611-816e-000d3a80c8b8;ParentContext=null;PostEntityImages={};PreEntityImages={};SecondaryEntityName=none;RequestId=null;Stage=20;UserId=e9cd027f-26a3-e511-80c6-000d3a807ec7;InputParameters={Query=Microsoft.Xrm.Sdk.Query.QueryExpressionColumnSet={ly_name,modifiedon,ly_minutessincecreated,createdon,ly_testid,processid,ly_name,ly_minutessincecreated,modifiedon,createdon,}LinkEntities={LinkEntity={}}}当前用户拥有角色:System Customizer,计划经理,计划员,系统管理员;查找的主实体是:ly_test;查询表达式是:<?xml version="1.0" encoding="utf-16"?>
<QueryExpression _tmplitem="3" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionData _tmplitem="3" ></ExtensionData>
<Distinct _tmplitem="3" >false</Distinct>
<NoLock _tmplitem="3" >true</NoLock>
<PageInfo _tmplitem="3" >
<PageNumber _tmplitem="3" >1</PageNumber>
<Count _tmplitem="3" >50</Count>
<ReturnTotalRecordCount _tmplitem="3" >true</ReturnTotalRecordCount>
<ExtensionData _tmplitem="3" ></ExtensionData>
</PageInfo>
<LinkEntities _tmplitem="3" >
<LinkEntity _tmplitem="3" >
<LinkFromAttributeName _tmplitem="3" >processid</LinkFromAttributeName>
<LinkFromEntityName _tmplitem="3" >ly_test</LinkFromEntityName>
<LinkToEntityName _tmplitem="3" >workflow</LinkToEntityName>
<LinkToAttributeName _tmplitem="3" >workflowid</LinkToAttributeName>
<JoinOperator _tmplitem="3" >LeftOuter</JoinOperator>
<LinkCriteria _tmplitem="3" >
<FilterOperator _tmplitem="3" >And</FilterOperator>
<Conditions _tmplitem="3" ></Conditions>
<Filters _tmplitem="3" ></Filters>
<IsQuickFindFilter _tmplitem="3" >false</IsQuickFindFilter>
<ExtensionData _tmplitem="3" ></ExtensionData>
</LinkCriteria>
<LinkEntities _tmplitem="3" />
<Columns _tmplitem="3" >
<AllColumns _tmplitem="3" >false</AllColumns>
<Columns _tmplitem="3" >
<string _tmplitem="3" >versionnumber</string>
</Columns>
<ExtensionData _tmplitem="3" ></ExtensionData>
</Columns>
<EntityAlias _tmplitem="3" >processidworkflowworkflowid</EntityAlias>
<Orders _tmplitem="3" ></Orders>
<ExtensionData _tmplitem="3" ></ExtensionData>
</LinkEntity>
</LinkEntities>
<Criteria _tmplitem="3" >
<FilterOperator _tmplitem="3" >And</FilterOperator>
<Conditions _tmplitem="3" ></Conditions>
<Filters _tmplitem="3" ></Filters>
<IsQuickFindFilter _tmplitem="3" >false</IsQuickFindFilter>
<ExtensionData _tmplitem="3" ></ExtensionData>
</Criteria>
<Orders _tmplitem="3" >
<OrderExpression _tmplitem="3" >
<AttributeName _tmplitem="3" >ly_name</AttributeName>
<OrderType _tmplitem="3" >Ascending</OrderType>
<ExtensionData _tmplitem="3" ></ExtensionData>
</OrderExpression>
</Orders>
<EntityName _tmplitem="3" >ly_test</EntityName>
<ColumnSet _tmplitem="3" >
<AllColumns _tmplitem="3" >false</AllColumns>
<Columns _tmplitem="3" >
<string _tmplitem="3" >ly_name</string>
<string _tmplitem="3" >modifiedon</string>
<string _tmplitem="3" >ly_minutessincecreated</string>
<string _tmplitem="3" >createdon</string>
<string _tmplitem="3" >ly_testid</string>
<string _tmplitem="3" >processid</string>
<string _tmplitem="3" >ly_name</string>
<string _tmplitem="3" >ly_minutessincecreated</string>
<string _tmplitem="3" >modifiedon</string>
<string _tmplitem="3" >createdon</string>
</Columns>
<ExtensionData _tmplitem="3" ></ExtensionData>
</ColumnSet>
<TopCount _tmplitem="3" xsi:nil="true" ></TopCount>
</QueryExpression>
然后到主题了,我这假设限制罗勇测试实体的十进制数列(ly_decimal)不允许导出,就在这个实体的RetrieveMultiple消息的Pre阶段使用如下代码,当然我还做了个限制,没有系统管理员角色不能导出,只是为了证明这里面可以做很多事情,用权限来控制导出功能,很简单吧:
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System.Linq;
using System.Collections.Generic; namespace CrmVSSolution.Plugins
{
public class PreTestRetrieveMultiple : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService)); IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); //context.Depth = 2是导出的时候执行
if (context.Depth == && context.InputParameters.Contains("Query") && context.InputParameters["Query"] is QueryExpression)
{
if (!GetUserRoles(service, context.UserId).Contains("系统管理员"))
{
throw new InvalidPluginExecutionException("不是管理员不能导出!");//这个错误提示不会展示在前台给用户看到
}
QueryExpression objQueryExpression = (QueryExpression)context.InputParameters["Query"];
objQueryExpression.ColumnSet = new ColumnSet(objQueryExpression.ColumnSet.Columns.Where(col => col != "ly_decimal").ToArray());
}
} public IEnumerable<string> GetUserRoles(IOrganizationService service, Guid userId)
{
string fetchXml = string.Format(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true' no-lock='true'>
<entity name='role'>
<attribute name='name' />
<link-entity name='systemuserroles' from='roleid' to='roleid' visible='false' intersect='true'>
<link-entity name='systemuser' from='systemuserid' to='systemuserid' alias='ad'>
<filter type='and'>
<condition attribute='systemuserid' operator='eq' value='{0}' />
</filter>
</link-entity>
</link-entity>
</entity>
</fetch>", userId);
EntityCollection entities = service.RetrieveMultiple(new FetchExpression(fetchXml));
return entities.Entities.Select(x => x.GetAttributeValue<string>("name"));
}
}
}
在界面上可以看到十进制数列有值:
导出来的时候这个列就没有值:
这样虽然可以导出罗勇测试实体的时候不到处十进制字段,但是我如果查询的是它的子实体,然后加上这个实体的十进制字段显示,这个用高级查找很容易做到,导出的话就可以看到这个列了。
 
这个时候我需要在这个实体的1:N关系的子实体中也在RetrieveMultiple消息的Pre阶段注册类似如下插件代码即可:
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System.Linq; namespace CrmVSSolution.Plugins
{
public class PreTestSubRetrieveMultiple : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService)); IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); //context.Depth=2是导出的时候执行
if (context.Depth == && context.InputParameters.Contains("Query") && context.InputParameters["Query"] is QueryExpression)
{
QueryExpression objQueryExpression = (QueryExpression)context.InputParameters["Query"];
if (objQueryExpression.LinkEntities != null) //当这个不能导出的实体作为关联的父实体加入了视图列,也需要过滤
{
foreach (var linkentity in objQueryExpression.LinkEntities.Where(x => x.LinkToEntityName == "ly_test"))
{
linkentity.Columns = new ColumnSet(linkentity.Columns.Columns.Where(col => col != "ly_decimal").ToArray());
}
}
}
}
}
}

然后我可以看到效果,这个列的值导不出来了。

根据 Distinguish page load and Export to Excel in CRM 2015 的答案,最好是查看其ParentContext是否为null,若不为null再看其名称是否为ExportToExcel或者ExportDynamicToExcel,这个应该更加靠谱。
public void Execute(IServiceProvider serviceProvider)
{
var executionContext = serviceProvider.GetService<IPluginExecutionContext>();
var parentContext = executionContext.ParentContext;
if (parentContext != null &&
(parentContext.MessageName == "ExportToExcel" ||
parentContext.MessageName == "ExportDynamicToExcel"))
{
// Place your logic here
}
}
当然可能还有其他方法可以导出,我说的利用标准的功能不需要开发,欢迎各位看官提出来,看看能否封堵住。
 

定制Dynamics CRM标准导出功能:不能导出指定列的值的更多相关文章

  1. Dynamics CRM的Associate功能

    Dynamics CRM有一种特殊的关联关系叫Associate,一般常见于为用户分配角色.给团队添加用户.团队添加角色.队列添加用户等等.在一些特定场景下我们不可能把所有的操作都通过手动来完成尤其是 ...

  2. Dynamics CRM 通过RetrieveEntityRibbonRequest和RetrieveApplicationRibbonRequest导出实体的Ribbon XML

    今天看到勇哥的博客介绍了两个request指令用来导出实体的Ribbon XML,在没有工具之前编辑ribbon都是手工导出xml然后编辑的对于很多一开始接触CRM就用工具的人可能不是很熟悉.查了下这 ...

  3. Dynamics CRM 插件Plugin中获取和更新时间字段值的准确转换

    前面两篇介绍了后台代码通过组织服务获取更新时间字段.窗体javascript通过Odata获取更新时间字段,最后篇来实验下在插件中的获取和更新时间字段是否需要时制的转化,为何说是最后篇呢,因为在CRM ...

  4. oracle导入导出功能

    1.普通版:oracle导入导出功能:导出exp 用户名/密码@SID file=f:\xx.dmp owner=用户名 导入imp 用户名/密码@SID full=y file=f:\xx.dmp ...

  5. Dynamics CRM 2016 的新特性

    新版本CRM (2016 with update 0.1) 发布已有几个月了,总结一下新特性,从几个方面来看: 1. 针对整合功能的新特性 (1) 增加了CRM App for Outlook. 这个 ...

  6. Dynamics CRM实体系列之图表

    本节开始讲解Dynamics CRM的图表功能.任何产品基本上都会有数据分析的工具,Dynamics CRM当然也不例外,作为一个专门做销售管理的软件数据分析自然也是对于销售管理者的决策有很大的作用的 ...

  7. Dynamics CRM导出数据到Excel

    原创地址:http://www.cnblogs.com/jfzhu/p/4276212.html 转载请注明出处 Pivot Table是微软BI的一个重要工具,所以这里讲一下Dynamics CRM ...

  8. Dynamics CRM 报表导出EXCEL 列合并问题的解决方法

    CRM中的报表导出功能提供了多种格式,excel就是其中之一,这次遇到的问题是导出后打开excel列明合并的问题,具体如下看着相当不美观,物料名称字段占了AB两列,品牌占了CD两列等等. 该问题的源头 ...

  9. Dynamics CRM 修改Excel 最大导出记录限制及 最大上传文件限制

    CRM默认的Excel最大导出记录是10000条,最大上传文件限制为5m. 这样的限制可以满足少量数据的批量更新,但是如果数据量比较大的话需要修改最大的导出记录限制,和上传文件的大小,网上有的是直接修 ...

随机推荐

  1. .net core 拦截器的使用

    .net core 拦截器的使用 实例代码:https://gitee.com/D_C_L/InterceptorTest.git 拦截器主要是将程序中的一些可以独立出去的模块进行拦截,比如幕等性,和 ...

  2. 利用Azure虚拟机安装Dynamics 365 Customer Engagement之二:创建域控虚拟机

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  3. 由导入paramkio包失败,而pip list又能查到此包,而引出的:离线安装python第三方库的实用方法:解决公司内网,服务器/电脑不能上网却需要安装python三方库问题(下:Linux环境中)

    问题描述: 公司的Linux服务器是内网,今天要实现某个功能,会用到python控制ssh,需要安装一个Paramkio库,和上一篇一样,仅仅依靠Pypi上下载下来的离线.whl安装包是不行的,lin ...

  4. python 错误信息是:sudo :apt-get:command not found

    1.问题描述 错误信息是:sudo :apt-get:command not found 2.问题原因及解决 在centos下用yum install xxx yum和apt-get的区别一般来说著名 ...

  5. Redis 找出大 key

    系统: CentOS 7.4 64bit yum install python-pip gcc gcc-c++ python-devel git tmux -y pip install rdbtool ...

  6. 【心得】Lattice EPC3 PCS使用经验

    [博客导航] [导航]FPGA相关 一.目的 将使用过程中遇到的问题随时记录,共享经验心得. 二.心得随笔 1.仿真脚本 为避免生成PCS IP之后,仿真时忘记添加参数文件,可以在仿真脚本中添加以下代 ...

  7. SSL握手中win xp和SNI的那点事

    SSL握手中win xp和SNI的那点事 一.背景需求server1-3使用不同的域名对外提供https服务,用nginx作为前端负载均衡器并负责https集中解密工作(以用户访问的域名为依据进行流量 ...

  8. java8-03-Lambda表达式总结

    Lambda 表达式的语法格式      基本结构        ()   ->  {}     左侧   参数列表     右侧   方法体 (Lambda体)             1.无 ...

  9. Vue动态设置Dom元素宽高

    需求: slider侧边栏是宽度是动态的,使用jquery可以操作dom元素,设置宽高,但vue是避免操作dom的 <template> <div class="slide ...

  10. 如何在Markdown格式下插入动图/gif

    上传GIF动图与上传普通图片是一样的,都需要以下在markdown语法中 ![]() 的小括号内填写图片的地址.问题在于如何获取本地gif的地址呢? 核心的东西就是要把这个gif动图传上网络,这样图片 ...