当前位置:网站首页>Dynamics 365: explain virtual entity from 0 to 1
Dynamics 365: explain virtual entity from 0 to 1
2022-07-24 15:40:00 【Stone-hdj】
from Dynamics 365 for Customer Engagement 9.0 Start , Virtual entities pass through Dynamics 365 Customer Engagement Seamlessly represent data as entities in , It realizes the data integration in the external system . It does not require data replication , There is usually no need for custom coding .
Virtual entities have the following limitations , But in addition to the following limitations , It is no different from other custom entities :
- The data is read-only . Virtual entity attribute is not supported in Dynamics 365 in CE The changes are being pushed back to the external system
- Only organization level permissions of entities are supported . Field level security is not supported
- For external data , Need to be abstractly modeled as D365 Those fields supported by , For example, you want to get the name of a record in the external system , Gender , Age field , So in a virtual entity , You need to create and name , Field types that match the gender and age fields , such as text, number, optionset, date, image, and lookup type
- The record of external data must have a GUID The primary key of the format is associated with the primary key in the virtual entity
- Cannot use calculated field , If you need to calculate , Need to be done in an external system or in data provider Intermediate processing .
- You cannot filter or sort the values of a column
- I won't support it Audit History
- Not for queue Enable virtual entities
- Virtual entities do not support BPF
- Once a virtual entity is created, it cannot be changed to an ordinary entity
1. Create a plug-in and register
This plug-in will implement Retrieve and Retrieve Multiple, The sample code is connected to another organization D365 in , obtain Account Record in the entity and display it .
Will these hardcode Writing to your plug-in is not a good way , I do this mainly to make it easier for you to see how it works , You need to properly handle these configuration information when writing your own code
For how to use the configuration information in the following code, see Azure Where can I find , You can refer to this blog below
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 = "[email protected]";
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 = "[email protected]";
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);
}
}
}
}
Note that the code above uses Newtonsoft This third party Nuget package , So we need to use ILMerge perhaps Costura.Fody To pack it into a dll.
Register plug-ins ( No need to register step, Follow up creation Data Provider It will be automatically added when MainOperation This step)

2. register Data Provider
Plugin Registeration Tool: Register -> Register New Data Provider


Solution: Here we choose to create a new solution, It will be filled in by us Data Source Entity( Virtual entity )
Data Source Entity: Choose to create a new data source
Assembly: Select the plug-in we just registered
Retrieve and Retrieve Multiple: Also choose what we just created
After registration, we will see , In the first step, the plug-in we created will be automatically filled in

And we found that the steps inside are not editable

3. Newly created in the system Data Source
Setting -> Administration -> Virtual Entity Data Sources

Select the... We created in step 2 above Data Provider

Fill in Name And save 
4. Create virtual entities
The entity needs to put Virtual Entity Choose ,Data Source Select the data source we just created

Note: The fields in this step should correspond to those in the above code , Here's the picture


5. effect
Let's use advanced search to test , Advanced lookup will call Retrieve Multiple

Because we do filtering in the code , Extract only A At the beginning Account
When we click on one of the records to open , It will be called Retrieve

Reference link :
Get started with virtual entities (Developer Guide for Dynamics 365 Customer Engagement) | Microsoft Docs
https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/virtual-entities/get-started-ve?view=op-9-1#limitations-of-virtual-entities Configure virtual entities using custom data sources - Microsoft MVP(15-18) Luo Yong - Blog Garden (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/
边栏推荐
- 各种Normalization的直观理解
- [machine learning basics] - another perspective to explain SVM
- 【机器学习基础】——另一个视角解释SVM
- [shaders realize pixelate mosaic effect _shader effect Chapter 7]
- 【洛谷】P1908 逆序对
- iptables常用命令小清单
- Hard core innovation that database needs to care about in the future
- Reentrantlock reentrant lock
- vscode常用快捷键
- Analysys analysis "2022 China data security market data monitoring report" was officially launched
猜你喜欢

JMeter - call the interface for uploading files or pictures

什么是防火墙?防火墙能发挥什么样的作用?

Istio1.12:安装和快速入门

Still using listview? Use animatedlist to make list elements move

celery 启动beat出现报错ERROR: Pidfile (celerybeat.pid) already exists.

Do you understand the working principle of gyroscope?

【着色器实现Pixelate马赛克效果_Shader效果第七篇】

MySQL学习笔记(总结)

简化理解:发布订阅
![[quantitative test]](/img/df/f2d8b252169213af340f3e535bddef.png)
[quantitative test]
随机推荐
C# SQLite Database Locked exception
Lsyncd 实时同步
报错【项目报错】
vscode常用快捷键
You are only one SQL statement away from the tdengine Developer Conference!
Arduino ide esp32 firmware installation and upgrade tutorial
MySQL source code analysis -- data structure of index
公钥私钥传输,以及对CA证书的理解
Yolo5face: why reinvent the face detector
Analysis of some difficulties in VAE (variational self encoder)
[tf.keras]: a problem encountered in upgrading the version from 1.x to 2.x: invalidargumenterror: cannot assign a device for operation embedding_
OpenMP入门
yolov3 训练自己的数据集
Citic securities account opening process, is it safe to open an account on your mobile phone
Error reporting [project error reporting]
Lsyncd搭建同步镜像-用Lsyncd实现本地和远程服务器之间实时同步
简化理解:发布订阅
Five principles of solid are indispensable for good architecture design
Lsyncd real time synchronization
C. Recover an RBS