
Dynamics 365 插件运行的“流水线”到底长什么样?
Table of contents
Open Table of contents
1.简介
说插件执行管道前,我先从它的上层开始说,也就是“事件框架(Event framework)”。在使用Dynamics 365 这款产品时,除了使用它自身的标准功能外,每个公司的业务都有自己奇奇怪怪的需求,光靠标准的功能肯定不够,这个时候就需要我们写代码来扩展它,这就是事件框架,也就是 MS 官方提供给我们做这件事的基础。
我们可以把它理解成一个插件或自定义逻辑的基础,它负责做以下几件事:
- 让我们写代码,然后上传到系统里。不管是创建一条记录,还是更新一个订单,我们都可以在这些操作“前、中、后”添加自己的逻辑(就是我们平时说的插件或者工作流)
- 支持同步跑,也支持异步跑。
- 同步(Synchronous):比如保存客户时,必须等你的校验逻辑执行完,数据才真正存进去,用户要等着
- 异步(Asynchronous):不耽误用户操作,我们的逻辑放到后台慢慢排队执行,适合发送邮件、同步第三方系统这类不太着急的活
- 代码统一托管在数据库里。把写好的插件或工作流活动部署到 Dynamics 365 的数据库里,系统会自动把它同步到数据中心的所有服务器上,不用你一台一台去手动部署,省心
总结下来就是“事件框架”是 Dynamics 365 官方给我们开的一条后门,让我们能在系统核心操作发生时插入自己的业务逻辑,而且支持同步和异步两种玩法,部署也方便,是二次开发的基础。另外,Microsoft Dynamics 365 服务器和 Microsoft Outlook 客户端支持事件框架,如果你是想改页面UI、加按钮什么的,那不归事件框架管,这是前端自定义的范畴。
2.事件执行管道 Event execution pipeline
事件执行管道说白了就是个流水线,我刚开始接触 Dynamics 365 的时候,有被这个词唬到过,现在回想起来就想笑。好了,言归正传:当我们在系统里点保存按钮时,或者代码里调用了某个更新的接口,系统就会产生一个“消息包”(里面装着我们要存的数据和要干嘛的指令),然后把这个包丢到流水线上,这条流水线有2个哥们:
- Dynamics 365
- 我们注册的插件
大家按顺序轮流对这个“消息包”看一眼,甚至改一改,最后处理完再返回给调用的人。
TIP
只有通过组织服务和OData端点触发的事件才能触发插件,其他接口不行
体系结构和相关组件
下图是 Microsoft Dynamics 365 平台的整体体系结构,以及同步和异步事件处理

事件执行管道阶段(流水线的4个阶段)
事件管道分为多个阶段,其中有 4 个阶段可用于注册自定义开发的插件或第三方插件。在插件注册期间可以进一步在每个阶段对在该阶段中注册的多个插件进行排序(排名)
| 事件 Event | 阶段名称 Stage name | 阶段编号 Stage number | 说明 Description |
|---|---|---|---|
| 前期事件 Pre-Event | 预验证 Pre-validation | 10 | 这个阶段在“权限检查”之前执行,所以即使你没有操作权限,这里的代码也能跑 |
| 前期事件 Pre-Event | 预操作 Pre-operation | 20 | 在系统真正写数据库之前执行,这个阶段在事务里,报错就会全部回滚,数据不会变 |
| 平台核心操作 Platform Core Operation | 主操作 MainOperation | 30 | 系统真正执行增删改查的地方,这个阶段不让注册插件,是平台自己用的 |
| 后期事件 Post-Event | 后操作 Post-operation | 40 | 数据库已经改完了,事务还没提交 |
消息是怎么传递的?
- 用户操作触发 —> 系统生成一个
OrganizationRequest消息(装着数据+指令) - 消息从管道一头进入
- 按阶段顺序依次经过:10 → 20 → 30(平台自己)→ 40
- 在每个阶段里,如果有多个插件,按你设置的排名(Rank)从小到大依次执行
- 前面的插件可以修改消息里的数据,后面拿到的就是改过之后的
- 走到30号阶段,平台执行真正的数据库操作
- 操作完成后,消息变成OrganizationResponse(响应结果)
- 继续传给40号阶段的插件,它们可以读取或修改响应结果
- 最后响应返回给调用者(用户界面或代码)
关于事务,最最容易踩坑的地方
- 阶段10(预验证):默认不在事务里。但也不是绝对的,你可以通过
IPluginExecutionContext里的IsInTransaction属性来判断当前是否在事务中。 - 阶段20(预操作)和阶段40(后操作):一定在事务里
IPluginExecutionContext executionContext = Context.PluginExecutionContext;
bool isInTransaction = executionContext.IsInTransaction;
if (executionContext.Stage == 10)
{
// isInTransaction --> false
}
else if (executionContext.Stage == 20 || executionContext.Stage == 40)
{
// isInTransaction --> true
}
这意味着如果在阶段20或40的插件里抛出异常,那么:
- 整个数据库事务会回滚,数据恢复原样
- 平台上要干的核心操作(比如创建记录)也会被取消
- 已经排好队但还没执行的其他插件和工作流,也都不会跑了
(简单说就是在20或40阶段报错 = 全剧终,什么都白干)
TIP
不管你同步还是异步,插件的执行时间最长只有2分钟。超时就报System.TimeoutException。如果你的逻辑确实要跑很久,就别用插件了,考虑用工作流或者其他后台任务。这个限制只针对“沙盒”模式下的插件,大部分自己写的插件都是这种。
3.沙盒隔离、信任级别和运行监控
沙盒是个啥?为啥要有它?
你可以把沙盒想象成一个带护栏的儿童游乐区——插件在里面可以随便玩,但翻不过去那道墙。具体来说:
- 能做的事:正常调用D365的SDK,读写业务数据,跟外面一样
- 不能做的事:访问硬盘文件、写系统日志、改注册表、用某些网络协议(比如TCP)——这些都是被墙挡住的
- 一个例外:可以访问Azure云服务这类外部端点(这个门是开着的)

为什么要这么设计?因为 D365 Online 是微软帮我们管的服务器,我们写的插件是第三方代码,万一写崩了、死循环了、把服务器搞挂了怎么办?所以放在沙盒里跑,再崩也只影响这个沙盒进程,不会把整个平台拖垮。
监控机制 PluginTypeStatistic
微软会在运行时收集沙盒里跑的所有插件的统计数据,存在数据库的 PluginTypeStatistic 实体里。跑完大概30分钟到1小时,这些数据就会生成,我们可以通过查询接口去查看。更重要的是:系统还会看门!比如说我们在沙盒里写了个非常拉跨的插件,插件执行的时候会出现 CPU占用过高、内存超载,甚至是干脆不响应了,这个时候系统会直接把这个进程杀掉,里面正在跑的插件全部报错失败。
放心,这不是永久封杀。下次再触发这个插件时,它会正常启动运行,不会一直挂掉。另外,每个组织都有自己的进程,你家组织挂了不会影响到别家的组织(这点在 Online 多租户环境里特别重要,比如有问题的插件在开发环境上,这就不会影响到测试机或者正式机)
信任级别(Trusts)
注册插件时,可以选择两种模式:
| 模式 | 官方术语 | 通俗解释 |
|---|---|---|
| 沙盒模式 | 部分信任(Partial Trust) | 插件在带护栏的游乐场里跑,受限制但安全。 |
| 非沙盒模式 | 完全信任(Full Trust) | 插件没有护栏,想干啥干啥,权限全开 |
区别:
- D365 Online(在线版):只支持沙盒模式。你必须把插件注册成沙盒,否则不让跑
- D365 本地部署(On-premises):两种都支持,你自己选。但官方强烈推荐用沙盒模式,因为更安全、有监控、在所有部署类型里都兼容
沙盒里怎么访问外部网站?
沙盒插件可以通过HTTP和HTTPS协议访问外部网络,比如调第三方API、抓取网页数据等。但是有以下规定:
- 只允许HTTP和HTTPS,其他协议不行
- 不能访问localhost(本机回环地址),别想绕
- 不能用IP地址(比如 192.168.1.1),必须用域名(比如 api.example.com),因为要走DNS解析
- 只支持匿名认证,不支持保存用户凭据或弹窗让用户登录。所以如果你要调的外部接口需要认证,得自己把Token写死在代码里或者从配置里取
这些规定系统管理员可以进行修改,访问规则存在服务器注册表中,路径:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\SandboxWorkerOutboundUriPattern。默认值是一长串正则表达式,它规定了哪些网址能访问、哪些不能。管理员可以根据需要修改这个值来放宽或收紧限制。不过一般开发者不需要动这个,知道有这么回事就行
(完)
如果这篇文章刚好帮到了你,欢迎请我喝杯咖啡!