为了解决WCF不能直接序列化 SqlParameter
类型的问题,可以采用以下步骤:
1. 自定义 DataContractResolver
SqlParameter
类型不能被WCF直接序列化,需要自定义 DataContractResolver
以解决该问题。在自定义 DataContractResolver
的过程中,需要使用一些类来处理实际的序列化和反序列化操作。
首先,定义两个辅助类 SqlParameterSurrogate
和 SqlParameterSurrogateSelector
。前者用于存储 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
的信息,包括 ParameterName
,Direction
,SqlDbType
,Value
,Size
这五个属性。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技术站