示例:具有CRUD操作的自定义虚拟表提供程序

此示例演示如何实现自定义数据提供程序来创建支持创建、检索、更新和删除作的虚拟表。 对于上述每个作,你将实现一个通用插件,使用插件注册工具注册它们,并启用虚拟表数据源来创建虚拟表。

若要详细了解数据提供程序和插件开发,请参阅 自定义数据提供程序

数据源详细信息

在本演练中,你将在外部 SQL Server 中设置一个简单的表来创建虚拟表。 此示例中使用的表名称为 VETicket

注释

如果要更改表或列的名称,请更新插件代码。

列名 数据类型 目的
TicketID 唯一标识符、主键 表的主键。
Severity 整数 工单的严重性值。
Name String 工单的说明。

可通过四个步骤使自定义数据提供程序能够创建虚拟表。

步骤 1:实现 CRUD 插件并注册程序集

步骤 2:创建数据提供程序并将插件添加到提供程序

步骤 3:在 Dataverse 环境中创建虚拟表

步骤 4:使用虚拟表创建、更新、查看和删除记录

步骤 1:实现 CRUD 插件并注册程序集

  1. 创建插件项目并安装以下 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
  2. 将以下六个类文件添加到解决方案。 在每个类文件中添加以下 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. 
            }
        }
    }
}
  1. 编译并生成解决方案。 现在,你将拥有一个程序集文件(.dll),可用于在 Dataverse 环境中注册。 将在 解决方案文件夹/bin/Debug 目录中找到此文件。

    程序集 dll。

  2. 使用插件注册工具注册程序集。 可以从 NuGet 获取最新的插件注册工具包。

  3. 打开插件注册工具。 需要拥有系统管理权限才能注册程序集。选择 “新建连接 ”以连接到 Dataverse 环境。 选择“ 注册 ”下拉列表,然后选择“ 注册新程序集”。

    注册新步骤。

  4. 选择程序集文件并注册插件。请确保已选中所有插件(创建、更新、删除、检索及 RetrieveMultiple 插件)。

    注册新程序集。

步骤 2:创建数据提供程序并将插件添加到提供程序

  1. 选择“ 注册 ”下拉列表,然后选择“ 注册新数据提供程序”。

  2. “注册新数据提供程序 ”对话框中,输入以下详细信息:

    1. 输入 数据提供程序名称

    2. “解决方案 ”选项中,选择现有解决方案或在下拉列表中创建新解决方案。 如果要创建新解决方案,请从下拉列表中选择 “NewSolution ”选项。 在“ 新建解决方案 ”对话框中,输入所需的详细信息,然后选择“ 保存”。

    3. “数据源表”(实体) 选项中,选择“ 创建新数据源”。 输入详细信息。 确保数据源是你创建或选择的解决方案的一部分。

      注释

      Dataverse 中的数据源表保存要传递给提供程序插件的数据源记录的配置数据。

    4. 将每个已注册插件映射到其各自的操作。

    5. 注册新的数据提供程序。

      注册数据提供程序。

  3. 在插件注册工具中,你将看到新的数据源记录和关联的数据提供程序。 选择数据源将显示包括插件及其注册 GUID 的详细信息。

    已注册的数据提供程序。

步骤 3:在 Dataverse 环境中创建虚拟表

  1. 导航到 设置>管理>虚拟表(实体)数据源,然后创建新的虚拟表数据源。

  2. 选择“ 新建 ”,然后从下拉列表中选择在上一步中创建的数据提供程序。

  3. 输入数据源的名称,然后选择“ 保存并关闭”。

  4. 现在,可以创建表示外部数据源的虚拟表。 为此,请转到 “设置>自定义系统”。

  5. 在解决方案资源管理器的左侧导航窗格中,选择 表(实体),然后选择“ 新建”。

  6. 输入以下详细信息:

    Description
    数据源 选择在上一步中创建的数据源。
    显示名称 虚拟表名称。
    复数名称 该值将根据显示名称自动填充。
    Name 系统将根据您输入的显示名称自动生成该名称。
    外部名称 源表的名称。
    外部集合名称 可以使用复数名称列中的相同值。
  7. 选择“ 保存并关闭”。

    创建新记录。

  8. 在左侧导航窗格中,选择并展开所创建的虚拟表。

  9. 选择 “字段 ”以更新并创建新列,表示外部源。

  10. 选择虚拟表的主 列,然后选择“ 编辑”。

  11. 更新 “外部名称 ”列以匹配外部数据源中的列名称。 在此示例中,外部列名称为 TicketID

    创建新表。

  12. 选择保存并关闭

  13. 选择虚拟表的 “名称 ”字段,然后选择“ 编辑”。

  14. 更新 “外部名称” 字段以匹配外部数据源中的字段名称。 在此示例中,外部列名称为 Name

    创建新名称字段。

  15. 选择保存并关闭

  16. 选择“ 新建 ”以在虚拟表中创建新列。 此列将代表外部数据源中的严重性列。

  17. 请为新列输入以下信息:

    列名 价值
    显示名称 Severity
    Name new_severity
    外部名称 Severity
    字段要求 业务必需的
    数据类型 整数

    创建新的严重性字段。

  18. 选择保存并关闭

步骤 4:使用虚拟表创建、更新、查看和删除记录

创建模型驱动应用并将虚拟表添加到站点地图。 然后选择虚拟表主窗体和高级字段视图。 发布应用。 详细信息: 从头开始生成第一个模型驱动应用

创建模型驱动应用。

应用程序用户可以像Microsoft Dataverse 中的其他任何表一样,使用虚拟表执行读取、创建、更新、删除作。

另请参阅

虚拟表入门
虚拟表的 API 注意事项
自定义虚拟表数据提供程序
使用 OData v4 数据提供程序的虚拟表演练