此示例演示如何实现自定义数据提供程序来创建支持创建、检索、更新和删除作的虚拟表。 对于上述每个作,你将实现一个通用插件,使用插件注册工具注册它们,并启用虚拟表数据源来创建虚拟表。
若要详细了解数据提供程序和插件开发,请参阅 自定义数据提供程序
数据源详细信息
在本演练中,你将在外部 SQL Server 中设置一个简单的表来创建虚拟表。 此示例中使用的表名称为 VETicket。
注释
如果要更改表或列的名称,请更新插件代码。
| 列名 | 数据类型 | 目的 |
|---|---|---|
| TicketID | 唯一标识符、主键 | 表的主键。 |
| Severity | 整数 | 工单的严重性值。 |
| Name | String | 工单的说明。 |
可通过四个步骤使自定义数据提供程序能够创建虚拟表。
步骤 1:实现 CRUD 插件并注册程序集
创建插件项目并安装以下 NuGet 包。 此示例中的解决方案名为 StubProvider。
集会 URL Microsoft.CrmSdk.CoreAssemblies https://www.nuget.org/packages/Microsoft.CrmSdk.CoreAssemblies Microsoft.CrmSdk.Data https://www.nuget.org/packages/Microsoft.CrmSdk.Data Microsoft.CrmSdk.Deployment https://www.nuget.org/packages/Microsoft.CrmSdk.Deployment Microsoft.CrmSdk.Workflow https://www.nuget.org/packages/Microsoft.CrmSdk.Workflow Microsoft.CrmSdk.XrmTooling.CoreAssembly https://www.nuget.org/packages/Microsoft.CrmSdk.XrmTooling.CoreAssembly Microsoft.IdentityModel.Clients.ActiveDirectory https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory Microsoft.Rest.ClientRuntime https://www.nuget.org/packages/Microsoft.Rest.ClientRuntime Newtonsoft.Json https://www.nuget.org/packages/Newtonsoft.Json/13.0.1-beta2 将以下六个类文件添加到解决方案。 在每个类文件中添加以下 using 语句
using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Extensions; using Microsoft.Xrm.Sdk.Data.Exceptions; using Newtonsoft.Json;注释
在上述每个类文件中,更新表名称以匹配设置的源表名称。 该示例使用 VETicket 作为源表名称。
类文件名 目的 Connection.cs 此类包含用于创建与外部 SQL 数据源的连接并对其进行管理的代码。 它包括特定于外部数据库的连接字符串参数和建立连接所需的基于 SQL 的身份验证信息。 替换以下对应值:数据库服务器、UserID、密码以及您将在 Dataverse 中创建虚拟表的表名。 CreatePlugin.cs 此类包含处理虚拟表创建操作的代码。 UpdatePlugin.cs 此类包含处理虚拟表中记录更新的代码。 RetrievePlugin.cs 此类包含从虚拟表中检索特定记录的代码。 RetrieveMultiplePlugin.cs 此类包含从虚拟表中批量获取记录的代码。 DeletePlugin.cs 此类包含删除虚拟表中记录的代码。
阅读以下有关在应用程序代码中使用连接字符串或用户名/密码身份验证的重要信息。
重要
Microsoft建议使用可用的最安全的身份验证流。 本文中所述的身份验证流要求在应用程序中高度信任,并且存在其他流中不存在的风险。 仅当其他更安全的流(如托管标识)不可行时,才应使用此流。
Connection.cs 的代码
public static class Connection
{
public static SqlConnection GetConnection()
{
try
{
//sample database to connect to
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "Enter name or network address of the SQL Server";
builder.UserID = "Enter User Name";
builder.Password = "Enter password";
builder.InitialCatalog = "Enter database details";
SqlConnection connection = new SqlConnection(builder.ConnectionString);
return connection;
}
catch (SqlException e)
{
Console.WriteLine(e.ToString());
throw;
}
}
}
CreatePlugin.cs代码
public class CreatePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.Get<IPluginExecutionContext>();
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
Guid id = Guid.NewGuid();
//change the table name below to the source table name you have created
string cmdString = "INSERT INTO VETicket (TicketID,Name,Severity) VALUES (@TicketID, @Name, @Severity)";
SqlConnection connection = Connection.GetConnection();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = cmdString;
command.Parameters.AddWithValue("@TicketID", id);
command.Parameters.AddWithValue("@Name", entity["new_name"]);
command.Parameters.AddWithValue("@Severity", entity["new_severity"]);
connection.Open();
try
{
var numRecords = command.ExecuteNonQuery();
Console.WriteLine("inserted {0} records", numRecords);
}
finally
{
connection.Close();
}
// other codes.
}
context.OutputParameters["id"] = id;
}
}
}
UpdatePlugin.cs代码
public class UpdatePlugin: IPlugin {
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.Get<IPluginExecutionContext>();
Guid id = Guid.Empty;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
//change the table name below to the source table name you have created
string cmdString = "UPDATE VETicket SET {0} WHERE TicketID=@TicketID";
SqlConnection connection = Connection.GetConnection();
using (SqlCommand command = connection.CreateCommand())
{
command.Parameters.AddWithValue("@TicketID", entity["new_ticketid"]);
List<string> setList = new List<string>();
if (entity.Attributes.Contains("new_name"))
{
command.Parameters.AddWithValue("@Name", entity["new_name"]);
setList.Add("Name=@Name");
}
if (entity.Attributes.Contains("new_severity"))
{
command.Parameters.AddWithValue("@Severity", entity["new_severity"]);
setList.Add("Severity=@Severity");
}
command.CommandText = string.Format(cmdString, string.Join(",", setList)); connection.Open();
try
{
var numRecords = command.ExecuteNonQuery();
Console.WriteLine("updated {0} records", numRecords);
}
finally
{
connection.Close();
}
// other codes.
}
}
}
}
RetrievePlugin.cs代码
public class RetrievePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.Get<IPluginExecutionContext>();
Guid id = Guid.Empty;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is EntityReference)
{
EntityReference entityRef = (EntityReference)context.InputParameters["Target"];
Entity e = new Entity("new_ticket");
//change the table name below to the source table name you have created
string cmdString = "SELECT TicketID, Severity, Name FROM VETicket WHERE TicketID=@TicketID";
SqlConnection connection = Connection.GetConnection();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = cmdString;
command.Parameters.AddWithValue("@TicketID", entityRef.Id);
connection.Open();
try
{
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
e.Attributes.Add("new_ticketid", reader.GetGuid(0));
e.Attributes.Add("new_severity", reader.GetInt32(1));
e.Attributes.Add("new_name", reader.GetString(2));
}
}
}
finally
{
connection.Close();
}
// other codes.
}
context.OutputParameters["BusinessEntity"] = e;
}
}
}
RetrieveMultiplePlugin.cs代码
public class RetrieveMultiplePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.Get<IPluginExecutionContext>();
EntityCollection collection = new EntityCollection();
//change the table name below to the source table name you have created
string cmdString = "SELECT TicketID, Severity, Name FROM VETicket";
SqlConnection connection = Connection.GetConnection();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = cmdString;
connection.Open();
try
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Entity e = new Entity("new_ticket");
e.Attributes.Add("new_ticketid", reader.GetGuid(0));
e.Attributes.Add("new_severity", reader.GetInt32(1));
e.Attributes.Add("new_name", reader.GetString(2));
collection.Entities.Add(e);
}
}
}
finally
{
connection.Close();
}
context.OutputParameters["BusinessEntityCollection"] = collection;
}
}
}
DeletePlugin.cs代码
public class DeletePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.Get<IPluginExecutionContext>();
//comment
Guid id = Guid.Empty;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is EntityReference)
{
EntityReference entityRef = (EntityReference)context.InputParameters["Target"];
id = entityRef.Id;
//change the table name below to the source table name you have created
string cmdString = "DELETE VETicket WHERE TicketID=@TicketID";
SqlConnection connection = Connection.GetConnection();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = cmdString; command.Parameters.AddWithValue("@TicketID", id);
connection.Open();
try
{
var numRecords = command.ExecuteNonQuery();
Console.WriteLine("deleted {0} records", numRecords);
}
finally
{
connection.Close();
}
// other codes.
}
}
}
}
编译并生成解决方案。 现在,你将拥有一个程序集文件(.dll),可用于在 Dataverse 环境中注册。 将在 解决方案文件夹/bin/Debug 目录中找到此文件。
使用插件注册工具注册程序集。 可以从 NuGet 获取最新的插件注册工具包。
打开插件注册工具。 需要拥有系统管理权限才能注册程序集。选择 “新建连接 ”以连接到 Dataverse 环境。 选择“ 注册 ”下拉列表,然后选择“ 注册新程序集”。
选择程序集文件并注册插件。请确保已选中所有插件(创建、更新、删除、检索及 RetrieveMultiple 插件)。
步骤 2:创建数据提供程序并将插件添加到提供程序
选择“ 注册 ”下拉列表,然后选择“ 注册新数据提供程序”。
在 “注册新数据提供程序 ”对话框中,输入以下详细信息:
输入 数据提供程序名称。
在 “解决方案 ”选项中,选择现有解决方案或在下拉列表中创建新解决方案。 如果要创建新解决方案,请从下拉列表中选择 “NewSolution ”选项。 在“ 新建解决方案 ”对话框中,输入所需的详细信息,然后选择“ 保存”。
在 “数据源表”(实体) 选项中,选择“ 创建新数据源”。 输入详细信息。 确保数据源是你创建或选择的解决方案的一部分。
注释
Dataverse 中的数据源表保存要传递给提供程序插件的数据源记录的配置数据。
将每个已注册插件映射到其各自的操作。
注册新的数据提供程序。
在插件注册工具中,你将看到新的数据源记录和关联的数据提供程序。 选择数据源将显示包括插件及其注册 GUID 的详细信息。
步骤 3:在 Dataverse 环境中创建虚拟表
导航到 设置>管理>虚拟表(实体)数据源,然后创建新的虚拟表数据源。
选择“ 新建 ”,然后从下拉列表中选择在上一步中创建的数据提供程序。
输入数据源的名称,然后选择“ 保存并关闭”。
现在,可以创建表示外部数据源的虚拟表。 为此,请转到 “设置>自定义系统”。
在解决方案资源管理器的左侧导航窗格中,选择 表(实体),然后选择“ 新建”。
输入以下详细信息:
列 Description 数据源 选择在上一步中创建的数据源。 显示名称 虚拟表名称。 复数名称 该值将根据显示名称自动填充。 Name 系统将根据您输入的显示名称自动生成该名称。 外部名称 源表的名称。 外部集合名称 可以使用复数名称列中的相同值。 选择“ 保存并关闭”。
在左侧导航窗格中,选择并展开所创建的虚拟表。
选择 “字段 ”以更新并创建新列,表示外部源。
选择虚拟表的主 键 列,然后选择“ 编辑”。
更新 “外部名称 ”列以匹配外部数据源中的列名称。 在此示例中,外部列名称为 TicketID。
选择保存并关闭。
选择虚拟表的 “名称 ”字段,然后选择“ 编辑”。
更新 “外部名称” 字段以匹配外部数据源中的字段名称。 在此示例中,外部列名称为 Name。
选择保存并关闭。
选择“ 新建 ”以在虚拟表中创建新列。 此列将代表外部数据源中的严重性列。
请为新列输入以下信息:
列名 价值 显示名称 Severity Name new_severity 外部名称 Severity 字段要求 业务必需的 数据类型 整数
选择保存并关闭。
步骤 4:使用虚拟表创建、更新、查看和删除记录
创建模型驱动应用并将虚拟表添加到站点地图。 然后选择虚拟表主窗体和高级字段视图。 发布应用。 详细信息: 从头开始生成第一个模型驱动应用
应用程序用户可以像Microsoft Dataverse 中的其他任何表一样,使用虚拟表执行读取、创建、更新、删除作。