Dynamics 365: 详解虚实体(Virtual Entity) 从0到1

从Dynamics 365 for Customer Engagement 9.0开始,虚实体通过在Dynamics 365 Customer Engagement中无缝地将数据表示为实体,实现了外部系统中的数据集成。它无需数据复制,通常也无需自定义编码。

虚实体有如下的限制,但除了下面的这些限制外,它和其它的那些自定义实体没有区别:

  • 数据是只读的。虚实体特性不支持在 Dynamics 365中 CE所做的更改在推回到外部系统
  • 只支持实体的组织级权限。不支持字段级安全
  • 对于外部数据,需要抽象建模为D365的支持的那些字段,就比如说你想获取外部系统中一条记录的姓名,性别,年龄字段,那么在虚实体中,你需要创建与姓名,性别和年龄字段相符的字段类型,比如text, number, optionset, date, image, 和lookup类型
  • 外部数据的记录必须有一个GUID格式的主键与虚实体中的主键相关联
  • 无法使用计算字段,如果需要计算,需要在外部系统完成或者在data provider中进行处理。
  • 无法筛选某一列的值或者进行对其进行排序
  • 不支持Audit History
  • 不能为queue启用虚实体
  • 虚实体不支持BPF
  • 虚实体一旦创建就不能更改为普通实体

1. 创建插件并注册

这个插件将会实现Retrieve和Retrieve Multiple,示例代码中是连接到另外一个组织的D365中,获取Account实体里的记录并显示出来.

将这些hardcode写到你的插件中并不是一个很好的方法,我这样做主要是为了让你可以更容易看到它是如何工作的,你在编写自己的代码的时候需要将这些配置信息妥善处理一下

对于如何下面代码中的这些配置信息在Azure中的什么地方能够找到,大家可以参考下面的这篇博客

Dynamics 365: 如何从Azure中获取连接到D365 Online所需要的认证信息_Stone-hdj的博客-CSDN博客https://blog.csdn.net/djstone/article/details/124927079?spm=1001.2014.3001.5502

Retrieve:

using Microsoft.Xrm.Sdk;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;

namespace VirtualEntityODataProvider
{
    public class Retrieve : IPlugin
    {
        //set these values for your D365 instance, user credentials and Azure AD clientid/token endpoint
        string crmorg = "https://orgeXXXX.crm5.dynamics.com";
        string clientid = "b004872a-XXXX-XXXX-XXXX-4a21868c04db";
        string username = "applicationuser@XXXX.onmicrosoft.com";
        string userpassword = "XXXX";
        string tokenendpoint = "https://login.microsoftonline.com/594a2057-XXXX-XXXX-XXXX-0bc293dfb025/oauth2/token";
        string clientsecret = "mTQ8Q~2bc84~fMK5Z0qc123456XXXdaG";

        //relative path to web api endpoint
        string crmwebapi = "/api/data/v9.2";

        string crmwebapipath = "/accounts({Id})?$select=name,accountid";

        public void Execute(IServiceProvider serviceProvider)
        {
            //basic plugin set-up stuff
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory servicefactory = XXXX(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = servicefactory.CreateOrganizationService(context.UserId);
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            tracingService.Trace($"Starting Tracing!");

            try
            {
                EntityReference target = (EntityReference)context.InputParameters["Target"];

                //build the authorization request for Azure AD
                var reqstring = "client_id=" + clientid;
                reqstring += "&client_secret=" + clientsecret;
                reqstring += "&resource=" + Uri.EscapeUriString(crmorg);
                reqstring += "&username=" + Uri.EscapeUriString(username);
                reqstring += "&password=" + Uri.EscapeUriString(userpassword);
                reqstring += "&grant_type=password";

                //make the Azure AD authentication request
                WebRequest req = WebRequest.Create(tokenendpoint);
                req.ContentType = "application/x-www-form-urlencoded";
                req.Method = "POST";
                byte[] bytes = System.Text.Encoding.ASCII.GetBytes(reqstring);
                req.ContentLength = bytes.Length;
                System.IO.Stream os = req.GetRequestStream();
                os.Write(bytes, 0, bytes.Length);
                os.Close();

                tracingService.Trace($"Request token completed");
                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                StreamReader tokenreader = new StreamReader(resp.GetResponseStream());
                string responseBody = tokenreader.ReadToEnd();
                tokenreader.Close();

                tracingService.Trace($"Parsing token response");
                //deserialize the Azure AD token response and get the access token to supply with the web api query
                var tokenresponse = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(responseBody);
                var token = tokenresponse["access_token"];
                tracingService.Trace($"token:{token}");
                //make the web api query
                WebRequest crmreq = WebRequest.Create(crmorg + crmwebapi + crmwebapipath.Replace("{Id}", target.Id.ToString()));
                crmreq.Headers = new WebHeaderCollection();

                //use the access token from earlier as the authorization header bearer value
                crmreq.Headers.Add("Authorization", "Bearer " + token);
                crmreq.Headers.Add("OData-MaxVersion", "4.0");
                crmreq.Headers.Add("OData-Version", "4.0");
                crmreq.Headers.Add("Prefer", "odata.maxpagesize=500");
                crmreq.Headers.Add("Prefer", "odata.include-annotations=OData.Community.Display.V1.FormattedValue");
                crmreq.ContentType = "application/json; charset=utf-8";
                crmreq.Method = "GET";

                HttpWebResponse crmresp = (HttpWebResponse)crmreq.GetResponse();
                StreamReader crmreader = new StreamReader(crmresp.GetResponseStream());
                string crmresponseBody = crmreader.ReadToEnd();
                crmreader.Close();
                tracingService.Trace($"Retrieve completed");
                //deserialize the response
                var crmresponseobj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(crmresponseBody);
                

                tracingService.Trace($"Retrieve result completed, crmresponseobj = {crmresponseobj}");
                Entity verow = new Entity("cr8e0_otheraccount");
                verow["cr8e0_otheraccountid"] = (Guid)crmresponseobj["accountid"];
                verow["cr8e0_name"] = (string)crmresponseobj["name"];

                //return a result
                context.OutputParameters["BusinessEntity"] = verow;
            }
            catch (Exception e)
            {
                tracingService.Trace($"{e.Message} {e.StackTrace}");
                if (e.InnerException != null)
                    tracingService.Trace($"{e.InnerException.Message} {e.InnerException.StackTrace}");

                throw new InvalidPluginExecutionException(e.Message);
            }
        }
    }
}

Retrieve Multiple:

using Microsoft.Xrm.Sdk;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;

namespace VirtualEntityODataProvider
{
    public class RetrieveMultiple : IPlugin
    {
        //set these values for your D365 instance, user credentials and Azure AD clientid/token endpoint
        string crmorg = "https://orgeXXXX.crm5.dynamics.com";
        string clientid = "b004872a-XXXX-XXXX-XXXX-4a21868c04db";
        string username = "applicationuser@XXXX.onmicrosoft.com";
        string userpassword = "XXXX";
        string tokenendpoint = "https://login.microsoftonline.com/594a2057-XXXX-XXXX-XXXX-0bc293dfb025/oauth2/token";
        string clientsecret = "mTQ8Q~2bc84~fMK5Z0qc123456XXXdaG";

        //relative path to web api endpoint
        string crmwebapi = "/api/data/v9.2";

        //web api query to execute - in this case all accounts that start with "A"
        string crmwebapipath = "/accounts?$select=name,accountid&$filter=startswith(name,'A')";

        public void Execute(IServiceProvider serviceProvider)
        {
            
            //basic plugin set-up stuff
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory servicefactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = servicefactory.CreateOrganizationService(context.UserId);
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            tracingService.Trace($"Starting Tracing!");

            try
            {
                EntityCollection results = new EntityCollection();

                //build the authorization request for Azure AD
                var reqstring = "client_id=" + clientid;
                reqstring += "&client_secret=" + clientsecret;
                reqstring += "&resource=" + Uri.EscapeUriString(crmorg);
                reqstring += "&username=" + Uri.EscapeUriString(username);
                reqstring += "&password=" + Uri.EscapeUriString(userpassword);
                reqstring += "&grant_type=password";

                //make the Azure AD authentication request
                WebRequest req = WebRequest.Create(tokenendpoint);
                req.ContentType = "application/x-www-form-urlencoded";
                req.Method = "POST";
                byte[] bytes = System.Text.Encoding.ASCII.GetBytes(reqstring);
                req.ContentLength = bytes.Length;
                System.IO.Stream os = req.GetRequestStream();
                os.Write(bytes, 0, bytes.Length);
                os.Close();

                tracingService.Trace($"Request token completed");
                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                StreamReader tokenreader = new StreamReader(resp.GetResponseStream());
                string responseBody = tokenreader.ReadToEnd();
                tokenreader.Close();

                tracingService.Trace($"Parsing token response");
                //deserialize the Azure AD token response and get the access token to supply with the web api query
                var tokenresponse = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(responseBody);
                var token = tokenresponse["access_token"];
                tracingService.Trace($"token:{token}");
                //make the web api query
                WebRequest crmreq = WebRequest.Create(crmorg + crmwebapi + crmwebapipath);
                crmreq.Headers = new WebHeaderCollection();

                //use the access token from earlier as the authorization header bearer value
                crmreq.Headers.Add("Authorization", "Bearer " + token);
                crmreq.Headers.Add("OData-MaxVersion", "4.0");
                crmreq.Headers.Add("OData-Version", "4.0");
                crmreq.Headers.Add("Prefer", "odata.maxpagesize=500");
                crmreq.Headers.Add("Prefer", "odata.include-annotations=OData.Community.Display.V1.FormattedValue");
                crmreq.ContentType = "application/json; charset=utf-8";
                crmreq.Method = "GET";

                HttpWebResponse crmresp = (HttpWebResponse)crmreq.GetResponse();
                StreamReader crmreader = new StreamReader(crmresp.GetResponseStream());
                string crmresponseBody = crmreader.ReadToEnd();
                crmreader.Close();
                tracingService.Trace($"Retrieve multiple completed");
                //deserialize the response
                var crmresponseobj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(crmresponseBody);

                //loop through the response values
                foreach (var row in crmresponseobj["value"].Children())
                {
                    //create a new virtual entity of type lpa_demove
                    Entity verow = new Entity("cr8e0_otheraccount");
                    verow["cr8e0_otheraccountid"] = (Guid)row["accountid"];
                    verow["cr8e0_name"] = (string)row["name"];

                    //add it to the collection
                    results.Entities.Add(verow);
                }

                tracingService.Trace($"Retrieve results completed, there are {results.Entities.Count} items!");
                //return the results
                context.OutputParameters["BusinessEntityCollection"] = results;
            }
            catch (Exception e)
            {
                tracingService.Trace($"{e.Message} {e.StackTrace}");
                if (e.InnerException != null)
                    tracingService.Trace($"{e.InnerException.Message} {e.InnerException.StackTrace}");

                throw new InvalidPluginExecutionException(e.Message);
            }
        }
    }
}

需要注意上面的代码中用到了Newtonsoft这个第三方的Nuget包,所以我们需要使用ILMerge或者Costura.Fody来打包成一个dll.

注册插件(不需要注册step,后续创建Data Provider的时候会自动添加MainOperation这个step)

2. 注册Data Provider

Plugin Registeration Tool: Register -> Register New Data Provider

Solution: 这里我们选择新建一个solution, 它里面会放我们下面填的Data Source Entity(虚实体)

Data Source Entity:选择新建一个data source

Assembly: 选择我们刚刚注册的插件

RetrieveRetrieve Multiple:也是选择我们我们刚刚创建的

注册完成后我们会看到,第一步我们创建的插件里的就会被自动填充步骤

 并且我们发现里面的步骤都是不可编辑的

 3. 系统内部新建Data Source

Setting -> Administration -> Virtual Entity Data Sources

选择我们上面第二步创建的Data Provider

 填写Name并保存

 4. 创建虚实体

实体里需要把Virtual Entity选中,Data Source选择我们刚刚创建的数据源

 Note:这一步里的字段需要和上面代码里的是对应上的,如下图

5. 效果

我们使用高级查找去测试一下,高级查找会调用Retrieve Multiple

 因为我们代码中做了过滤,只提取A开头的Account

 当我们点击其中一条记录来打开,它会调用Retrieve

参考链接:

Get started with virtual entities (Developer Guide for Dynamics 365 Customer Engagement) | Microsoft Docshttps://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/virtual-entities/get-started-ve?view=op-9-1#limitations-of-virtual-entities使用自定义数据源配置虚拟实体 - 微软MVP(15-18)罗勇 - 博客园 (cnblogs.com)https://www.cnblogs.com/luoyong0201/p/Dynamics_365_Develop_Virtual_Entity_with_Custom_Data_Provider.htmlUsing Dynamics 365 virtual entities to show data from an external organization (alexanderdevelopment.net)https://alexanderdevelopment.net/post/2018/05/28/using-dynamics-365-virtual-entities-to-show-data-from-an-external-organization/

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stone-hdj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值