D365平台与传统平台html
D365平台与传统平台,在功能和页面开发层面均有不一样之处。好比说在功能开发层面:传统平台须要本身开发,而365平台是系统标准的功能,不须要开发。在页面开发层面:传统平台须要本身开发,而365平台只需简单配置便可;shell
插件数据库
Plugin
(插件),它是一种事件处理程序,经过它能够修改或扩充Dynamics 365标准的业务流程,如建立时执行自定义逻辑。另外是SDK Message
(SDK消息),经过它能够获取CRM标准功能中Create
,Update
,Delete
等相关操做的事件信息;c#
执行顺序sass
有消息就会有事件前后的顺序,因此这边就会涉及两个名词,Pre-operation
和Post-operation
;Pre-operation
:消息事件触发前执行一个动做;Post-operation
:消息事件触发后执行一个动做;服务器
1.新建VS解决方案项目类库dom
首先须要建立一个 .NET Framework
的 Class Library
类库项目,这里要为不一样版本的Dynamics365
选择的Framework
不尽相同,请根据官方文档说明;异步
2.添加项目依赖包ide
经过NuGet
添加对Microsoft.CrmSdk.CoreAssemblies
的引用,以下图,固然也要选择合适的版本。若是不能上网的话,就须要添加对 Microsoft.Xrm.Sdk.dll
和 Microsoft.Crm.Sdk.Proxy.dll
的引用;工具
3.新建插件
插件文件本质也是类文件,只不过这个类继承IPlugin
接口,且实现Execute
方法,在此方法中编写插件代码,实现业务逻辑;建议:插件命名:功能/做用英文+实体名+Plugin
,例:CreateNew_StudentPlugin
;
代码能够看到Execute
方法只有一个输入参数serviceProvider
,该参数的类型是IServiceProvider
,是事件执行管道传递给当前插件的全部消息的容器,存储了在插件中可能要使用到的各种对象。经过IServiceProvider
接口的GetService
方法,能够获取执行上下文IPluginExecutionContext
、组织服务工厂IOrganizationServiceFactory
以及跟踪服务ITracingService
等实例;
public void Execute(IServiceProvider serviceProvider) { // 获取插件执行上下文 IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); // 获取组织服务工厂实例 IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); // 获取组织服务实例 IOrganizationService service = factory.CreateOrganizationService(context.UserId); // 获取跟踪服务 ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); try { // 插件业务逻辑代码 } catch (FaultException<OrganizationServiceFault> ex) { // 异常处理代码 } }
4.插件签名
在Visual Studio
中右击该项目,选择属性(Properties
) > 签名(Signing
),选中 Sign the assembly
,我这里新建一个Key file
Key file
个人设置以下,为了简便,我就不设置密码保护了,保存后编译插件项目,肯定没有编译错误
1.下载注册工具
从Dynamics 365 Customer Engagement (V9.0)
开始,再也不像之前同样提供SDK
下载了,应该学习在线文档 中下载方式,如下下载方式主要是根据在线文章中命令方式下载:
本文提到下载的工具包括以下:
Tool | NuGet Package |
---|---|
Code generation tool CrmSvcUtil.exe |
Microsoft.CrmSdk.CoreTools |
Configuration Migration tool DataMigrationUtility.exe |
Microsoft.CrmSdk.XrmTooling.ConfigurationMigration.Wpf |
Package Deployer PackageDeployer.exe |
Microsoft.CrmSdk.XrmTooling.PackageDeployment.WPF |
Plug-in Registration Tool PluginRegistration.exe |
Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool |
SolutionPackager tool SolutionPackager.exe |
Microsoft.CrmSdk.CoreTools |
打开 Windows PowerShell
,最好是以管理员身份打开
切换到你要下载工具的目录
执行以下的命令,执行完毕后记得回车
$sourceNugetExe = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $targetNugetExe = ".\nuget.exe" Remove-Item .\Tools -Force -Recurse -ErrorAction Ignore Invoke-WebRequest $sourceNugetExe -OutFile $targetNugetExe Set-Alias nuget $targetNugetExe -Scope Global -Verbose ## ##Download Plugin Registration Tool ## ./nuget install Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool -O .\Tools md .\Tools\PluginRegistration $prtFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool.'} move .\Tools\$prtFolder\tools\*.* .\Tools\PluginRegistration Remove-Item .\Tools\$prtFolder -Force -Recurse ## ##Download CoreTools ## ./nuget install Microsoft.CrmSdk.CoreTools -O .\Tools md .\Tools\CoreTools $coreToolsFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.CoreTools.'} move .\Tools\$coreToolsFolder\content\bin\coretools\*.* .\Tools\CoreTools Remove-Item .\Tools\$coreToolsFolder -Force -Recurse ## ##Download Configuration Migration ## ./nuget install Microsoft.CrmSdk.XrmTooling.ConfigurationMigration.Wpf -O .\Tools md .\Tools\ConfigurationMigration $configMigFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.ConfigurationMigration.Wpf.'} move .\Tools\$configMigFolder\tools\*.* .\Tools\ConfigurationMigration Remove-Item .\Tools\$configMigFolder -Force -Recurse ## ##Download Package Deployer ## ./nuget install Microsoft.CrmSdk.XrmTooling.PackageDeployment.WPF -O .\Tools md .\Tools\PackageDeployment $pdFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.PackageDeployment.Wpf.'} move .\Tools\$pdFolder\tools\*.* .\Tools\PackageDeployment Remove-Item .\Tools\$pdFolder -Force -Recurse ## ##Download Package Deployer PowerShell module ## ./nuget install Microsoft.CrmSdk.XrmTooling.PackageDeployment.PowerShell -O .\Tools $pdPoshFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.PackageDeployment.PowerShell.'} move .\Tools\$pdPoshFolder\tools\*.* .\Tools\PackageDeployment.PowerShell Remove-Item .\Tools\$pdPoshFolder -Force -Recurse ## ##Remove NuGet.exe ## Remove-Item nuget.exe
能够看到前面提到的工具都下载好了,一共是5个。若是要获取最新版本的工具,重复执行前面的步骤便可
2.注册/附加插件
打开插件注册工具
登陆-注册项目
新建插件
点击菜单栏 新建(Register) 选项,依次点击新建插件(Register New Assembly)
按照示例图片步骤一中选择DLL文件,在步骤二中勾选添加的插件(类),最后点击下方添加
新建步骤(插件执行时机)
点击菜单栏 新建(Register) 选项,依次点击新建步骤(Register New Step)
Message:执行时机,Primary Entity:关联实体,在何时执行
3.调试插件
安装调试工具
选中要调试的插件,选中调试的step
,点击Start Profiling
去CRM中操做实体,下载日志文件
回到插件注册器关掉profiling
点击Debug
到VS中,设置断点,点击调试=>附加PluginRegistration进程
再回到插件工具点击执行
这时就会看到断点生效,进行调试
开启/配置实例镜像
打开注册工具,展开注册的插件项目,右键点击建立的执行项,点击 建立新镜像选项
IPluginExecutionContext
插件执行上下文IPluginExecutionContext
中,包括有事件处理管道传递给插件的各种信息,包括执行插件的运行时环境、执行管道相关信息以及触发Web服务的实体实例信息。IPluginExecutionContext
接口中的成员列表以下所示:
名称 | 说明 |
---|---|
ParentContext | 从父管道操做中获取执行上下文信息。父子管道产生缘由在于CRM系统中某些消息请求可能会产生其余消息请求。举例来讲AssignRequest请求会产生一个UpdateRequest请求,若是两个插件A和U分别订阅了AssignRequest消息和UpdateRequest消息,那么在AssignRequest产生时,插件A、插件U将依次执行,此时插件U的执行上下文的ParentContext属性将被赋予插件A的执行上下文。 |
Stage | 获取同步执行模式插件在执行管道中所处的阶段 |
IExecutionContext
IPluginExecutionContext
接口继承自IExecutionContext
接口,在IExecutionContext
接口中,包含了大量的有关上下文的信息,以下表所示:
名称 | 说明 |
---|---|
BusinessUnitId | 获取执行管道所处理的实体实例的业务部门GUID。 |
CorrelationId | 该属性的用途CRM平台为了不出现无限死循环 |
Depth | 获取当前插件在调用堆栈中的深度。也是为了不出现无限死循环。插件开发人员能够在插件代码中对该属性进行判断从而避免出现无限死循环。常常的一种使用情景是,订阅了UpateRequest的插件代码中还执行了Update操做。 |
InitiatingUserId | 获取当前执行事件管道的系统用户的GUID. |
InputParameters | 获取触发插件执行的请求消息参数. |
IsExecutingOffline | 获取当前插件是否运行在脱机环境中 |
IsInTransaction | 获取插件是否执行在数据库事务中。 |
IsOfflinePlayback | 若是插件能够运行在脱机环境中,那么在客户端与CRM服务器同步时,极可能因为数据同步形成服务器端同一插件再次执行一遍,从而致使数据出现了错误,此时,就须要在插件中使用本数据判断,是不是因为离线客户端与CRM服务器同步触发了本插件的运行。 |
IsolationMode | 判断插件是否执行在沙盒Sandbox中。 |
MessageName | 获取当前事件管道所处理的请求消息 |
Mode | 获取插件执行模式:同步执行仍是异步执行. |
OperationCreatedOn | 在与Azure云进行集成的时候用到的。 |
OperationId | |
OrganizationId | 获取实体实例所属组织的GUID. |
OrganizationName | 获取实体实例所属组织的惟一名称. |
OutputParameters | 获取平台核心操做完成后的响应消息参数. |
OwningExtension | 获取相关联的SdkMessageProcessingingStep对象. |
PostEntityImages | 主要用于UpdateRequest消息中,分别获取核心操做以前以及以后实体的快照、映像信息 |
PreEntityImages | |
PrimaryEntityId | 事件管道正在处理的实体实例的GUID |
PrimaryEntityName | 事件管道正在处理的实体的逻辑名称. |
RequestId | 事件管道正在处理的请求的GUID. |
SharedVariables | 获取/设置插件之间传递的自定义属性值. |
提示:
targer
是否可用插件基本初始化类
using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Client; using System; namespace T4 { public abstract class PluginBase : IPlugin { #region 相关服务 //调试沙箱插件使用的跟踪服务 protected ITracingService tracingservice; //插件的上下文 protected IPluginExecutionContext context; //组织服务工厂 protected IOrganizationServiceFactory serviceFactory; //组织服务 protected IOrganizationService service; //组织服务 protected IOrganizationService serviceAdmin; protected OrganizationServiceContext orgServiceContext; //相关记录 protected Entity targer; protected Entity pretarger; protected Entity posttarger; protected EntityReference targerref; protected EntityReferenceCollection targerrefc; protected Relationship targerrel; protected EntityReference assignee; //触发操做 protected bool isCreate; protected bool isUpdate; protected bool isDelete; protected bool isAssociate; protected bool isDisassociate; protected bool isAssign; #endregion public void Execute(IServiceProvider serviceProvider) { Initialize(serviceProvider); DoExecute(serviceProvider); } public abstract void DoExecute(IServiceProvider serviceProvider); /// <summary> /// 插件入口 /// </summary> /// <param name="serviceProvider"></param> public void Initialize(IServiceProvider serviceProvider) { #region 相关服务的初始化 tracingservice = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); // service = serviceFactory.CreateOrganizationService(context.UserId); service = serviceFactory.CreateOrganizationService(null); //service = serviceFactory.CreateOrganizationService(null); serviceAdmin = serviceFactory.CreateOrganizationService(null); //orgServiceContext = new OrganizationServiceContext(service); isCreate = context.MessageName == "Create"; isUpdate = context.MessageName == "Update"; isDelete = context.MessageName == "Delete"; string contextMessage = context.MessageName; if (contextMessage == "Assign" || context.MessageName == "Associate" || context.MessageName == "Disassociate") return; if (context.InputParameters.Contains("Target")) { if (context.InputParameters["Target"] is Entity) targer = (Entity)context.InputParameters["Target"]; else if (context.InputParameters["Target"] is EntityReference) targerref = (EntityReference)context.InputParameters["Target"]; } #endregion } /// <summary> /// 插件入口 /// </summary> /// <param name="serviceProvider"></param> public void InitializeAssociate(IServiceProvider serviceProvider) { #region 相关服务的初始化 tracingservice = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); service = serviceFactory.CreateOrganizationService(context.UserId); serviceAdmin = serviceFactory.CreateOrganizationService(null); orgServiceContext = new OrganizationServiceContext(service); isAssociate = context.MessageName == "Associate"; isDisassociate = context.MessageName == "Disassociate"; targerref = (EntityReference)context.InputParameters["Target"]; targerrefc = (EntityReferenceCollection)context.InputParameters["RelatedEntities"]; targerrel = (Relationship)context.InputParameters["Relationship"]; #endregion } /// <summary> /// 插件入口 /// </summary> /// <param name="serviceProvider"></param> public void InitializeAssign(IServiceProvider serviceProvider) { #region 相关服务的初始化 tracingservice = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); service = serviceFactory.CreateOrganizationService(context.UserId); serviceAdmin = serviceFactory.CreateOrganizationService(null); orgServiceContext = new OrganizationServiceContext(service); //tracingservice.Trace("相关服务的初始化"); isAssign = context.MessageName == "Assign"; //tracingservice.Trace("相关对象初始化Target"); targerref = (EntityReference)context.InputParameters["Target"]; //tracingservice.Trace("相关对象初始化Assignee"); assignee = (EntityReference)context.InputParameters["Assignee"]; //tracingservice.Trace("相关对象初始化 ok"); #endregion } } }
新建插件
提示:
DateTime
类型Money
类型OptionSetValue
类型EntityReference
类型using Microsoft.Xrm.Sdk; using System; namespace T4 { public class CreatePlugin : PluginBase { public override void DoExecute(IServiceProvider serviceProvider) { if (context.Depth > 1) return; // 示例一 Random random = new Random(); Entity entity = new Entity(targer.LogicalName); entity["new_age"] = random.Next(18, 99); entity["new_name"] = $"学员:"+ random.Next(1000,9999); entity["new_gender"] = true; EntityReference entityReference = new EntityReference(targer.LogicalName, Guid.Parse("2867B927-B12F-EB11-B392-005056993F73")); entity["new_search"] = entityReference; entity["new_admissiondate"] = DateTime.UtcNow; service.Create(entity); // 示例二 Entity entitys = new Entity(targer.LogicalName); entitys["new_name"] = "YH";//单行文本类型 entitys["new_client_name"] = "Hello Word";//单行文本类型 entitys["new_client_id"] = 123456;//整形 entitys["new_client_float"] = 54.8;//浮点类型 entitys["new_client_sex"] = false;//两个选择 entitys["new_client_addtime"] = DateTime.Parse("2019-1-1 12:30:12");//日期类型 entitys["new_client_money"] = new Money(3000);//货币类型 entitys["new_client_cardnumber"] = decimal.Parse("50000");//十进制类型 entitys["new_client_summary"] = "尽心尽力";//多行文本类型 entitys["new_client_a"] = new OptionSetValue(100000000);//单项选项集 entitys["new_client_select"] = new EntityReference(targer.LogicalName, Guid.Parse("596B44E4-AF24-EB11-956F-E03F49115DFE"));//查找类型 service.Create(entitys); } } }
修改插件
提示:
using Microsoft.Xrm.Sdk; using System; namespace T4 { public class UpdatePlugin : PluginBase { public override void DoExecute(IServiceProvider serviceProvider) { if (context.Depth > 1) return; // 获取修改后实体镜像(修改后数据) posttarger = context.PostEntityImages["image"]; // 获取修改前实体镜像(修改前数据) pretarger = context.PreEntityImages["image"]; //判断字段是否存在 if (!posttarger.Contains("new_name")) return; Random random = new Random(); posttarger["new_age"] = random.Next(18, 99); posttarger["new_name"] = $"学员:" + random.Next(1000, 9999); posttarger["new_gender"] = true; // 根据实体名称,id建立查找类型值 EntityReference entityReference = new EntityReference(posttarger.LogicalName, Guid.Parse("2867B927-B12F-EB11-B392-005056993F73")); posttarger["new_search"] = entityReference; // 返回实体查找类型字段值,值为 EntityReference 类型 var entityReference = posttarger.GetAttributeValue<EntityReference>("new_search"); service.Update(posttarger); } } }
查询插件
// 单查询 var entity = service.Retrieve("实体名", Guid.NewGuid(), new Microsoft.Xrm.Sdk.Query.ColumnSet(true)); // 多查询 // 1.建立查询表达式,并执行查询实体 QueryExpression query = new QueryExpression(context.PrimaryEntityName); // 2.查询显示列名集合,true为所有显示 query.ColumnSet = new ColumnSet(true); // 2.条件筛选,格式:字段名,表达式,条件值 query.Criteria.AddCondition("字段名", ConditionOperator.Equal, "值"); // 3.执行查询 var collection = service.RetrieveMultiple(query); // 4.获取实体集合 var entity_list = collection.Entities;
删除插件
service.Delete(targer.LogicalName, targer.Id);// 当前实体名和实体名id