解决WCF不能直接序列化SqlParameter类型的问题

为了解决WCF不能直接序列化 SqlParameter 类型的问题,可以采用以下步骤:

1. 自定义 DataContractResolver

SqlParameter 类型不能被WCF直接序列化,需要自定义 DataContractResolver 以解决该问题。在自定义 DataContractResolver 的过程中,需要使用一些类来处理实际的序列化和反序列化操作。

首先,定义两个辅助类 SqlParameterSurrogateSqlParameterSurrogateSelector。前者用于存储 SqlParameter 的信息,后者用于选择当前类型是否要使用自定义的序列化方式。

[DataContract]
public class SqlParameterSurrogate
{
    [DataMember]
    public string ParameterName { get; set; }

    [DataMember]
    public ParameterDirection Direction { get; set; }

    [DataMember]
    public SqlDbType SqlDbType { get; set; }

    [DataMember]
    public object Value { get; set; }

    [DataMember]
    public int Size { get; set; }
}

public class SqlParameterSurrogateSelector : IDataContractSurrogate
{
    public bool CanSerialize(Type type, Type surrogateType)
    {
        return type == typeof(SqlParameter);
    }

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null;
    }

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
    {
        return null;
    }

    public Type GetDataContractType(Type type)
    {
        return typeof(SqlParameterSurrogate);
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        var surrogate = (SqlParameterSurrogate)obj;
        var parameter = new SqlParameter
        {
            ParameterName = surrogate.ParameterName,
            SqlDbType = surrogate.SqlDbType,
            Value = surrogate.Value,
            Direction = surrogate.Direction
        };
        if (surrogate.Size > 0)
        {
            parameter.Size = surrogate.Size;
        }

        return parameter;
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        var parameter = (SqlParameter)obj;
        return new SqlParameterSurrogate
        {
            ParameterName = parameter.ParameterName,
            SqlDbType = parameter.SqlDbType,
            Value = parameter.Value,
            Direction = parameter.Direction,
            Size = parameter.Size
        };
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return typeof(SqlParameterSurrogate);
    }

    public void ImportCallback(System.Runtime.Serialization.XmlObjectSerializer serializer, object deserializedObject)
    {
    }

    public void ProcessDataContractSurrogate(Type clrType, Type dataContractType, IDataContractSurrogateContext context)
    {
        context.DataContractSurrogate = this;
    }

    public object SetAndValidateUnknownAttribute(string name, string ns, object value, SerializationInfo info)
    {
        return null;
    }

    public bool ShouldSerialize(Type type, object obj)
    {
        return true;
    }
}

上述代码中,SqlParameterSurrogate 用于存储 SqlParameter 的信息,包括 ParameterNameDirectionSqlDbTypeValueSize 这五个属性。SqlParameterSurrogateSelector 则用于选择 SqlParameter 类型是否要使用自定义的序列化方式。可以看到,当序列化过程中遇到 SqlParameter 类型时,GetDataContractType 方法会返回 SqlParameterSurrogate 类型,GetObjectToSerialize 方法会将 SqlParameter 对象序列化成 SqlParameterSurrogate 对象。当反序列化过程中遇到 SqlParameterSurrogate 类型时,GetDeserializedObject 方法会将 SqlParameterSurrogate 对象反序列化成 SqlParameter 对象。

2. 配置 DataContractSerializer

经过以上自定义 DataContractResolver 的操作,我们需要将其应用于 WCF 的 DataContractSerializer 中以实现 SqlParameter 对象的序列化与反序列化,可以参考以下步骤:

var serializer = new DataContractSerializer(typeof(RequestMessage), new Type[] { typeof(SqlParameter) });
serializer.SetSerializationSurrogateProvider(new MySerializationSurrogateProvider());

在以上代码中:

  • RequestMessage 是自定义的消息类型,这里只是作为一个示例。
  • MySerializationSurrogateProvider 是自定义的 IDataContractSurrogateProvider 类型,用于提供 SqlParameterSurrogateSelector 对象。

示例1

具体来说,对于下面的WCF服务,如果存在存储过程 CreateOrder,那么我需要先将订单信息存到数据库。代码如下:

[ServiceContract]
public interface IOrderService
{
    [OperationContract]
    void CreateOrder(Order order);
}

public class Order
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public decimal Price { get; set; }
    public SqlParameter[] Parameters { get; set; }
}

public class OrderService : IOrderService
{
    public void CreateOrder(Order order)
    {
        const string connectionString = "Data Source=(local);Initial Catalog=Test;Integrated Security=True;";
        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
            var command = new SqlCommand("CreateOrder", connection)
            {
                CommandType = CommandType.StoredProcedure,
                CommandTimeout = 300
            };
            command.Parameters.Add(new SqlParameter("@CustomerName", SqlDbType.NVarChar, 50)).Value = order.CustomerName;
            command.Parameters.Add(new SqlParameter("@Price", SqlDbType.Decimal, 18)).Value = order.Price;
            foreach (var parameter in order.Parameters)
            {
                command.Parameters.Add(parameter);
            }
            command.ExecuteNonQuery();
        }
    }
}

可以看到,Order 类中包含了一个 SqlParameter[] 类型的属性,但是 WCF 默认情况下不能对该类型进行序列化。因此,我们需要按照上文所述实现自定义解决方案。修改客户端程序如下:

using (var client = new OrderServiceClient())
{
    client.Open();
    client.CreateOrder(new Order
    {
        CustomerName = "CustomerA",
        Price = 100,
        Parameters = new SqlParameter[]
        {
            new SqlParameter("@Notes", SqlDbType.NVarChar, 200).Value = "新订单",
            new SqlParameter("@IsUrgent", SqlDbType.Bit).Value = true
        }
    });
}

构造 Order 对象时,我们可以为其中的 SqlParameter[] 属性添加一些测试数据。

示例2

此外,我们还可以以 DataSet 或 DataTable 对象形式传递 SqlParameter 类型的参数,构造存储过程的测试代码如下:

public void CreateOrder(DataSet dataSet)
{
    const string connectionString = "Data Source=(local);Initial Catalog=Test;Integrated Security=True;";
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        var command = new SqlCommand("CreateOrderFromDataTable", connection)
        {
            CommandType = CommandType.StoredProcedure,
            CommandTimeout = 300
        };
        command.Parameters.Add("@orders", SqlDbType.Structured);
        command.Parameters["@orders"].Value = dataSet.Tables[0];
        command.ExecuteNonQuery();
    }
}

可以看到,在构造存储过程的 SqlCommand 对象时,我们为 @orders 参数指定了类型为 SqlDbType.Structured,因此我们需要将数据以 DataTable 或 DataSet 的形式传递。修改客户端程序如下:

using (var client = new OrderServiceClient())
{
    client.Open();
    var dataSet = new DataSet();
    var dataTable = dataSet.Tables.Add("OrderTable");
    dataTable.Columns.Add("OrderId", typeof(int));
    dataTable.Columns.Add("CustomerName", typeof(string));
    dataTable.Columns.Add("OrderDate", typeof(DateTime));
    dataTable.Rows.Add(1, "CustomerA", DateTime.Now);
    client.CreateOrder(dataSet);
}

在构造 DataTable 时,需要注意表结构的匹配。经过以上修改,就可以在 WCF 中直接使用 SqlParameter 类型的参数了。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解决WCF不能直接序列化SqlParameter类型的问题 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • C#加密算法汇总(推荐)

    C#加密算法汇总(推荐) 简介 本文主要介绍C#语言中常用的加密算法。涉及到的加密算法包括对称加密算法和非对称加密算法。对称加密算法包括DES、AES、RC4等,其中DES和AES已经成为最经典的对称加密算法,RC4算法常被用于数据流加密。非对称加密算法包括RSA和DSA等,其中RSA算法用于数字签名,DSA算法用于数字证书。 DES加密算法 DES加密算法…

    C# 2023年6月6日
    00
  • C# SynchronizationContext以及Send和Post使用解读

    C#中的SynchronizationContext是用于处理多线程并发问题的一种机制,它能够确保在多线程环境下的UI操作不会出现问题,保障了程序的稳定性和可靠性。在使用SynchronizationContext时,我们借助它提供的Send和Post方法来将指定的操作添加到指定的队列中,等待处理。 SynchronizationContext的作用 Syn…

    C# 2023年6月6日
    00
  • C#使用二维数组模拟斗地主

    C#使用二维数组模拟斗地主攻略 什么是二维数组 二维数组是数组的一种,与一维数组不同的是,它包含两个维度(行和列)。在编程中,可以使用二维数组来表示多个变量,比如一个矩阵。二维数组的定义方式如下: int[,] arr = new int[3,4]; 表示定义了一个由 3 行 4 列的整型数组。 斗地主游戏规则 斗地主是一种比较流行的扑克牌游戏。游戏有三个玩…

    C# 2023年6月7日
    00
  • c# 模拟串口通信 SerialPort的实现示例

    下面是关于“C#模拟串口通信SerialPort的实现示例”的攻略: 第一步:准备工作 在实现具体的代码之前,需要先准备一些基础工作。包括: 准备一个模拟串口的环境。这可以通过安装一个虚拟串口软件来实现(如“虚拟串口驱动程序”) 引入SerialPort类。在程序中需要使用System.IO.Ports命名空间,可以通过在程序中添加以下引用来实现:using…

    C# 2023年6月6日
    00
  • C# Diagnostics.Debug.WriteLine()方法: 将信息写入调试输出窗口

    C#中 Diagnostics.Debug.WriteLine() 的作用与使用方法 Diagnostics.Debug.WriteLine() 是一个用于输出调试信息的方法,可以在调试过程中将一些信息输出到控制台或者调试器中供开发者查看。 具体的说,Diagnostics.Debug.WriteLine() 方法会将输入的信息写入到调试器的输出窗口中,可以…

    C# 2023年4月19日
    00
  • c#字符串使用正则表达式示例

    下面是c#字符串使用正则表达式的完整攻略: 1. 使用正则表达式匹配字符串 使用c#中的正则表达式需要使用System.Text.RegularExpressions命名空间。下面是一个示例代码,其使用正则表达式匹配字符串,并将匹配到的结果输出到控制台: using System; using System.Text.RegularExpressions; …

    C# 2023年6月8日
    00
  • ASP.NET Core利用Jaeger实现分布式追踪详解

    在本攻略中,我们将详细讲解如何在ASP.NET Core中利用Jaeger实现分布式追踪,并提供两个示例说明。 安装Jaeger:首先,我们需要安装Jaeger。我们可以从官方网站下载Jaeger,并按照官方文档进行安装和配置。 安装Jaeger.Client:接下来,我们需要安装Jaeger.Client NuGet包。我们可以使用Visual Studi…

    C# 2023年5月16日
    00
  • C#动态创建Access数据库及密码的方法

    下面我将详细讲解“C#动态创建Access数据库及密码的方法”的完整攻略。 简介 Access数据库是一种常见的数据库类型,很多应用程序需要使用它来存储数据。在C#中,我们可以使用ADO.NET来连接Access数据库,而有时候我们需要动态地创建数据库,以及为数据库添加密码保护。接下来,我会给出动态创建Access数据库及密码的完整攻略。 步骤 1. 引用必…

    C# 2023年6月2日
    00
合作推广
合作推广
分享本页
返回顶部