解决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日

相关文章

  • ASP.NET Core依赖注入(DI)讲解

    ASP.NET Core依赖注入(DI)讲解 ASP.NET Core依赖注入(DI)是一种设计模式,它可以帮助您管理应用程序中的对象和它们之间的依赖关系。在本攻略中,我们将详细讲解ASP.NET Core依赖注入(DI)的概念、用法和示例。 什么是依赖注入(DI) 依赖注入(DI)是一种设计模式,它可以帮助您管理应用程序中的对象和它们之间的依赖关系。在DI…

    C# 2023年5月17日
    00
  • IIS7.5 检测到在集成的托管管道模式下不适用的 ASP.NET设置

    当在IIS 7.5中运行ASP.NET应用程序时,可能会遇到以下错误消息:“IIS7.5检测到在集成的托管管道模式下不适用的ASP.NET设置”。这个错误通常是由于应用程序的配置不正确所致。本文将提供解决此问题的完整攻略,包括错误原因、解决方案和示例。 错误原因 当在IIS 7.5中运行ASP.NET应用程序时,应用程序的配置必须与IIS的托管管道模式相匹配…

    C# 2023年5月15日
    00
  • C#简单判断字符编码的方法

    C# 中判断字符编码的方法可以使用 Encoding 类的 GetEncoding 方法,该方法可以将一个编码名称或编号转换为一个 Encoding 对象。接下来将详细讲解如何使用该方法来判断字符编码。 获取字符的字节数组 在判断字符编码之前,我们需要先将字符串转换为其字节数组,可以使用 Encoding 的 GetBytes 方法来实现。以下是一个简单的示…

    C# 2023年6月7日
    00
  • 简单聊一聊Go语言中的数组和切片

    简单聊一聊Go语言中的数组和切片 在Go语言中,数组和切片是两种常用的数据结构。本文将提供一个详细的Go语言中数组和切片的攻略,包括定义、初始化、访问、遍历、添加、删除等操作。 数组 定义和初始化 在Go语言中,数组是一种固定长度的数据结构,可以存储相同类型的元素。可以按照以下方式定义和初始化数组: var arr [5]int // 定义一个长度为5的in…

    C# 2023年5月15日
    00
  • 详解C#开发Android应用程序的流程

    详解C#开发Android应用程序的流程 一、开发环境配置 在开始C#开发Android应用程序之前,我们需要先配置好相应的开发环境: 1. 安装Visual Studio Visual Studio是C#程序开发的集成开发环境,可用于Windows和macOS平台。在Visual Studio官网下载对应操作系统的版本并安装。 2. 安装Visual St…

    C# 2023年6月7日
    00
  • C#保存图片到数据库并读取显示图片的方法

    整体思路 将图片转换为二进制,然后将二进制数据存储到数据库中,读取时从数据库中读取二进制数据,再将二进制数据转换为图片。 示范代码1:保存图片到数据库 首先,我们需要创建一个包含二进制数据的表格来存储图片。在该表格上创建两个字段:图片ID和图片内容。然后,使用下面的代码将图片转换为二进制数据,并将其插入到表格中: // 读取图片文件 FileStream f…

    C# 2023年6月2日
    00
  • C#把数字转换成大写金额的代码实例

    下面是详细的讲解C#将数字转换成大写金额的代码实例的攻略。 1. 需求分析 将数字转换成大写金额,是一个比较常见的需求,在开发中也经常会用到。对于这个需求,我们需要分析以下两个方面: 1.1 需要支持的数字类型:从整数到小数,都需要转换成大写金额。 1.2 转换后的金额格式:需要满足标准的中文货币格式。 2. 代码实现 为了将数字转换成大写金额,我们可以使用…

    C# 2023年6月7日
    00
  • 利用Visual Studio新建一个C# Web项目

    首先就是要下载Visual Studio,具体可以登录官网查看下载教程。 首次打开Visual Studio,就会显示新建的页面,若是使用过的则根据以下操作进行:  以下界面则与新使用的新建界面一致:选择ASP.NET Web,点击下一步 填写项目名称,选择项目存放的位置,选择框架,点击创建   在这里选择一个空的项目,然后一定要勾选右边的Web窗体选项,然…

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