解决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#使用Log4.net记录日志文件

    下面是使用 Log4net 记录 C# 日志的完整攻略: 步骤1:安装Log4net NuGet包 在 Visual Studio 中打开项目,右键单击解决方案,选择“管理NuGet包”,搜索 “log4net”,在搜索结果中选择“log4net”并安装。安装后会在项目文件夹中生成一个“packages”文件夹,其中包含log4net的DLL文件。 步骤2:…

    C# 2023年6月1日
    00
  • CommunityToolkit.Mvvm系列文章导航

    包 CommunityToolkit.Mvvm (又名 MVVM 工具包,以前名为 Microsoft.Toolkit.Mvvm) 是一个现代、快速且模块化的 MVVM 库。 它是 .NET 社区工具包的一部分,围绕以下原则构建: 平台和运行时独立 – .NET Standard 2.0、 .NET Standard 2.1 和 .NET 6? (UI Fr…

    C# 2023年4月17日
    00
  • C#实现网络小程序的步骤详解

    下面是详细讲解“C#实现网络小程序的步骤详解”的完整攻略。 1. 确定需求和功能 在开始任何项目之前,我们必须明确需求和目标。首先,确定你的网络小程序需要完成的功能,包括功能模块、界面设计等,以此为基础,设计项目结构和流程。 2. 确定开发环境 C#可以在Windows平台上运行,因此您需要安装Visual Studio等适用的软件开发工具。您需要安装.NE…

    C# 2023年6月1日
    00
  • C#仪器数据文件解析Excel文件的方法浅析(xls、xlsx)

    C#仪器数据文件解析Excel文件的方法浅析 在C#编程中,经常需要从仪器导出的数据文件进行Excel格式的解析。本文将针对xls和xlsx两种常见的Excel文件格式,分别进行简单的介绍。 Excel文件的格式说明 Excel文件主要包括两个文件格式,即xls和xlsx。其中,xls文件是Excel 97-2003版本的二进制文件格式,而xlsx文件是Ex…

    C# 2023年5月31日
    00
  • C#集合之字典的用法

    C#是一门强类型语言,拥有许多集合类型,字典(Dictionary)是其中最常用的之一。字典是一种键值对(Key-Value)的集合类型,可以通过键(key)快速地查找对应的值(value),同时也支持添加、删除、修改键值对等操作。 创建字典 在C#中创建字典可以使用Dictionary<TKey, TValue>类。TKey代表键的类型,TVa…

    C# 2023年5月31日
    00
  • C#实现简单的五子棋游戏

    C#实现简单的五子棋游戏攻略 1. 确定游戏规则和UI设计 五子棋游戏有一定规则,包括游戏开始、棋子下子、禁手判断、胜负判断、悔棋等。首先需要了解游戏规则,并设计好游戏的UI界面,包括游戏棋盘的布局、棋子的显示、提示信息等。 2. 建立游戏主体框架 在C#中,我们可以使用Windows窗体应用程序来实现五子棋游戏的UI设计和游戏主体框架的建立。具体步骤如下:…

    C# 2023年6月7日
    00
  • asp.net输出重写压缩页面文件实例代码

    ASP.NET是一款常用的Web应用程序开发框架,提供了很多优秀的功能。其中,输出重写和压缩页面文件也是ASP.NET的一个很重要的功能。下面,我将向大家详细讲解“asp.net输出重写压缩页面文件实例代码”的完整攻略。 一、什么是输出重写 ASP.NET中,输出重写是一种技术,可以动态地修改应用程序输出的HTML代码。当ASP.NET处理应用程序时,会生成…

    C# 2023年5月31日
    00
  • C#中的三种定时计时器Timer用法介绍

    下面我将为你详细讲解C#中的三种定时计时器Timer用法介绍的完整攻略。 1. 定时器Timer是什么? 定时器是一种常见的应用场景,比如日常使用的Android/IOS系统中的闹钟提醒功能、计数器功能等都需要定时器的支持。而在C#中,我们也可以使用定时器来实现某些需要定时执行的任务。 2. C#中的三种定时计时器Timer用法介绍 C#中,提供了三种常见的…

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