我最初在MessageContract中定义了一个SQLParameters集合,以便通过WCF服务创建一个简单的存储过程执行。显然,SQLParameter类型不可序列化,所以我需要一些关于如何继续这里的建议。
是否有可能仍以某种方式使用SQLParameter作为我的WCF合同的一部分,或者我必须做其他事情,比如创建一个与SQLParameter具有相同属性的自定义类,然后在我的代码中的其他地方创建SQLParameters?
更新
有关为什么会出现这种情况的进一步背景,最初Windows窗体客户端使用通常的ADO.NET对象直接连接到数据库以检索DataSet以进行报告。现在,客户想要一个通用的Web服务来处理所有报告。这是我能想到的最好的处理它而不需要太多的改变。
答案 0 :(得分:3)
我对接受的回应相对不感兴趣:
您可能会发现您希望进一步重构这些内容,以减少数量或提高抽象级别。但如果没有,那么你应该相当于将所有这些方法提取到一个或多个接口中。这些接口将成为WCF服务的ServiceContracts。将方法移动到新服务中以实现这些服务合同,您已经完成了很多工作。
从根本上说,这是对简单预定义业务逻辑的正确回应;但是,对于不同的抽象级别,比如运行 ad-hoc sql查询所需的服务,人们不能简单地通过预定义的服务调用提供这种级别的灵活性。
要使 ad-hoc 查询在WCF服务环境中工作,必须传递参数以保护系统并防止各种SQL注入样式攻击向量。
以防万一,我已经构建了一项服务,作为业务需求,需要将数据层从客户端抽象出来,并允许第三方与不同数据库系统上的许多数据库进行交互。
对于这个系统,我采用了Craig H的方法并创建了一个 SerializableSqlParam 类作为列表对象传递给我的服务。
我的 SerializableSqlParam 类的好处如下:
一般用法如下:
SerializedSqlParam sp = new SerializedSqlParam(new SqlParameter("@id", 1));
//or through typecasting:
SqlParameter parameter = new SqlParameter("@id", 1);
SerializedSqlParam sp = (SerializedSqlParam) parameter;
要反序列化,只需执行以下操作:
SqlParameter parameter = sp.GetSqlParameter();
//or through typecasting
SqlParameter parameter = (SqlParameter) sp;
这是我的班级。我确信有些东西可以修复/改进;然而,这只是为了让这个概念得以实现。希望其他读者会觉得这很有用!
<强> SerializedSqlParam.cs 强>
[DataContract]
public class SerializedSqlParam
{
[Browsable(false)]
[DataMember]
public string CompareInfo { get; set; }
[RefreshProperties(RefreshProperties.All)]
[DataMember]
public string Direction { get; set; }
[DataMember]
public bool IsNullable { get; set; }
[Browsable(false)]
[DataMember]
public int LocaleId { get; set; }
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[DataMember]
public int Offset { get; set; }
[DataMember]
public string ParameterName { get; set; }
[DefaultValue(0)]
[DataMember]
public byte Precision { get; set; }
[DefaultValue(0)]
[DataMember]
public byte Scale { get; set; }
[DataMember]
public int Size { get; set; }
[DataMember]
public string SourceColumn { get; set; }
[DataMember]
public bool SourceColumnNullMapping { get; set; }
[DataMember]
public string SourceVersion { get; set; }
[DataMember]
public string SqlDbType { get; set; }
[DataMember]
public string TypeName { get; set; }
[DataMember]
public string UdtTypeName { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember]
public string ValueType { get; protected set; }
[DataMember]
public string XmlSchemaCollectionDatabase { get; set; }
[DataMember]
public string XmlSchemaCollectionName { get; set; }
[DataMember]
public string XmlSchemaCollectionOwningSchema { get; set; }
public SerializedSqlParam(SqlParameter p)
{
this.CopyProperties(p);
this.SerializeParameterValue(p);
}
public static explicit operator SerializedSqlParam(SqlParameter p)
{
return new SerializedSqlParam(p);
}
public static explicit operator SqlParameter(SerializedSqlParam p)
{
return p.GetSqlParameter(p);
}
public SqlParameter GetSqlParameter()
{
return this.GetSqlParameter(this);
}
public SqlParameter GetSqlParameter(SerializedSqlParam serialized)
{
SqlParameter p = new SqlParameter();
p.ParameterName = serialized.ParameterName;
p.Precision = serialized.Precision;
p.Scale = serialized.Scale;
p.Size = serialized.Size;
p.IsNullable = serialized.IsNullable;
p.LocaleId = serialized.LocaleId;
p.Offset = serialized.Offset;
p.SourceColumn = serialized.SourceColumn;
p.SourceColumnNullMapping = serialized.SourceColumnNullMapping;
p.XmlSchemaCollectionDatabase = serialized.XmlSchemaCollectionDatabase;
p.XmlSchemaCollectionName = serialized.XmlSchemaCollectionName;
p.XmlSchemaCollectionOwningSchema = serialized.XmlSchemaCollectionOwningSchema;
p.TypeName = serialized.TypeName;
p.UdtTypeName = serialized.UdtTypeName;
p.Direction = (ParameterDirection)Enum.Parse(typeof(ParameterDirection), serialized.Direction);
p.CompareInfo = (SqlCompareOptions)Enum.Parse(typeof(SqlCompareOptions), serialized.CompareInfo);
p.SourceVersion = (DataRowVersion)Enum.Parse(typeof(DataRowVersion), serialized.SourceVersion);
p.Value = this.DeserializeObject(serialized.Value, Type.GetType(serialized.ValueType));
return p;
}
private void SerializeParameterValue(SqlParameter p)
{
if (p.Value.GetType().IsSerializable)
{
this.ValueType = this.GetTypeAssemblyQualifiedName(p.Value);
this.Value = this.SerializeObject(p.Value);
}
else
{
throw new SerializationException("Cannot serialize the parameter value object. Recast that object into a primitive or class that can be serialized.");
}
}
private void CopyProperties(SqlParameter p)
{
this.ParameterName = p.ParameterName;
this.Precision = p.Precision;
this.Scale = p.Scale;
this.Size = p.Size;
this.IsNullable = p.IsNullable;
this.LocaleId = p.LocaleId;
this.Offset = p.Offset;
this.SourceColumn = p.SourceColumn;
this.SourceColumnNullMapping = p.SourceColumnNullMapping;
this.XmlSchemaCollectionDatabase = p.XmlSchemaCollectionDatabase;
this.XmlSchemaCollectionName = p.XmlSchemaCollectionName;
this.XmlSchemaCollectionOwningSchema = p.XmlSchemaCollectionOwningSchema;
this.TypeName = p.TypeName;
this.UdtTypeName = p.UdtTypeName;
this.Direction = p.Direction.ToString();
this.CompareInfo = p.CompareInfo.ToString();
this.SourceVersion = p.SourceVersion.ToString();
try
{
this.SqlDbType = p.SqlDbType.ToString();
}
catch
{
this.SqlDbType = null;
}
}
private string SerializeObject(object value)
{
if (value == null) return null;
XmlSerializer serializer = new XmlSerializer(value.GetType());
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = new UnicodeEncoding(false, false);
settings.Indent = false;
settings.OmitXmlDeclaration = false;
using (StringWriter textWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.Serialize(xmlWriter, value);
}
return textWriter.ToString();
}
}
private object DeserializeObject(string xml, Type type)
{
if (string.IsNullOrEmpty(xml)) return null;
XmlSerializer serializer = new XmlSerializer(type);
XmlReaderSettings settings = new XmlReaderSettings();
using (StringReader textReader = new StringReader(xml))
{
using (XmlReader xmlReader = XmlReader.Create(textReader, settings))
{
return Convert.ChangeType(serializer.Deserialize(xmlReader), type);
}
}
}
private string GetTypeAssemblyQualifiedName(object obj)
{
return obj.GetType().AssemblyQualifiedName.ToString();
}
}
答案 1 :(得分:0)
首先 - 如果您希望通过WCF访问“数据库”,那么ADO.NET数据服务是一个更好的选择。
但不是;你不能通过WCF序列化SqlParameter;你需要将它封装在其他表示中。请注意,IMO将数据库逻辑暴露在如此接近WCF边界是非常危险的 - 我只有WCF方法来抽象它 - 即。
[OperationContract]
Customer[] FindCustomers(string id, string name, string location, ...);
然后你有一个严格控制的服务接口。
答案 2 :(得分:0)
听起来你正试图采取太多简单的方法。我将重构那些用于进行直接数据库访问的方法,主要是通过使用“提取方法”重构。这将留下一个(大量)小方法,每个方法接受一组参数并返回一个DataSet,每个方法都有一个特定目的。
您可能会发现您希望进一步重构这些内容,以减少数量或提高抽象级别。但如果没有,那么你应该相当于将所有这些方法提取到一个或多个接口中。这些接口将成为WCF服务的ServiceContracts。将方法移动到新服务中以实现这些服务合同,您已经完成了很多工作。
当然,这对于自动化单元测试和良好的代码覆盖率更有效。这将提供做出这种激进的事情所必需的信心。
答案 3 :(得分:0)
我刚刚为sqlparameter
创建了一个简单的序列化包装器#region
using System;
using System.Data;
using System.Data.SqlClient;
using System.Xml.Serialization;
#endregion
[Serializable]
public class SQLParamSerializationWrapper
{
#region Constants and Fields
private SqlParameter param;
#endregion
#region Constructors and Destructors
public SQLParamSerializationWrapper()
{
//paramless constructor for serialization
this.param = new SqlParameter();
}
public SQLParamSerializationWrapper(SqlParameter param)
{
this.SQLParam = param;
}
#endregion
#region Properties
public DbType DbType
{
get
{
return this.SQLParam.DbType;
}
set
{
this.SQLParam.DbType = value;
}
}
public ParameterDirection Direction
{
get
{
return this.SQLParam.Direction;
}
set
{
this.SQLParam.Direction = value;
}
}
public string ParameterName
{
get
{
return this.SQLParam.ParameterName;
}
set
{
this.SQLParam.ParameterName = value;
}
}
[XmlIgnore]
public SqlParameter SQLParam
{
get
{
return this.param;
}
set
{
this.param = value;
}
}
public int Size
{
get
{
return this.SQLParam.Size;
}
set
{
this.SQLParam.Size = value;
}
}
public object Value
{
get
{
return this.SQLParam.Value;
}
set
{
this.SQLParam.Value = value;
}
}
#endregion
}
然后您可以按如下方式使用它
序列化(即时通讯使用参数列表): -
List<SQLParamSerializationWrapper> procParams = new List<SQLParamSerializationWrapper>();
SqlParameter startdate = new SqlParameter("dateStart", new DateTime(2011, 9, 5));
SqlParameter enddate = new SqlParameter("dateEnd", new DateTime(2011, 9, 6));
SQLParamSerializationWrapper startDateWrapper = new SQLParamSerializationWrapper(startdate);
SQLParamSerializationWrapper endDateWrapper = new SQLParamSerializationWrapper(enddate);
procParams.Add(startDateWrapper);
procParams.Add(endDateWrapper);
string paramsAsXML = "";
using (var sw = new StringWriter())
{
using (var xw = XmlWriter.Create(sw))
{
XmlSerializer xs = new XmlSerializer(procParams.GetType());
xs.Serialize(xw, procParams);
}
paramsAsXML = sw.ToString();
}
反序列化: -
var procParams = new List<SqlParameter>();
StringReader sr = new StringReader(parm.Value);
// Create an instance of the XmlSerializer specifying type.
XmlSerializer deserializer = new XmlSerializer(typeof(List<SQLParamSerializationWrapper>));
List<SQLParamSerializationWrapper> sqlParamWrapper = (List<SQLParamSerializationWrapper>)deserializer.Deserialize(sr);
foreach (var param in sqlParamWrapper)
{
procParams.Add(param.SQLParam);
}