在WCF中序列化SQLParameter的任何解决方案或解决方法?

时间:2009-04-01 14:42:45

标签: .net wcf

我最初在MessageContract中定义了一个SQLParameters集合,以便通过WCF服务创建一个简单的存储过程执行。显然,SQLParameter类型不可序列化,所以我需要一些关于如何继续这里的建议。

是否有可能仍以某种方式使用SQLParameter作为我的WCF合同的一部分,或者我必须做其他事情,比如创建一个与SQLParameter具有相同属性的自定义类,然后在我的代码中的其他地方创建SQLParameters?

更新
有关为什么会出现这种情况的进一步背景,最初Windows窗体客户端使用通常的ADO.NET对象直接连接到数据库以检索DataSet以进行报告。现在,客户想要一个通用的Web服务来处理所有报告。这是我能想到的最好的处理它而不需要太多的改变。

4 个答案:

答案 0 :(得分:3)

我对接受的回应相对不感兴趣:

  

您可能会发现您希望进一步重构这些内容,以减少数量或提高抽象级别。但如果没有,那么你应该相当于将所有这些方法提取到一个或多个接口中。这些接口将成为WCF服务的ServiceContracts。将方法移动到新服务中以实现这些服务合同,您已经完成了很多工作。

从根本上说,这是对简单预定义业务逻辑的正确回应;但是,对于不同的抽象级别,比如运行 ad-hoc sql查询所需的服务,人们不能简单地通过预定义的服务调用提供这种级别的灵活性。

要使 ad-hoc 查询在WCF服务环境中工作,必须传递参数以保护系统并防止各种SQL注入样式攻击向量。

以防万一,我已经构建了一项服务,作为业务需求,需要将数据层从客户端抽象出来,并允许第三方与不同数据库系统上的许多数据库进行交互。

对于这个系统,我采用了Craig H的方法并创建了一个 SerializableSqlParam 类作为列表对象传递给我的服务。

我的 SerializableSqlParam 类的好处如下:

  1. SqlParameter类的直接序列化和类型转换。
  2. 序列化对象以UTF-16字符串格式存储,以允许SQL server to save the objects
  3. 正确使用AssemblyQualifiedName以允许对不在直接程序集中的对象进行反序列化。
  4. 完成SqlParameter类参数的编组。
  5. 一般用法如下:

    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);
                        }