现在的位置: 首页 > 综合 > 正文

WCF中因序列化问题引起的异常和错误。

2012年08月18日 ⁄ 综合 ⁄ 共 3061字 ⁄ 字号 评论关闭

这段时间一直在忙着赶项目,由DAL+WCF+WinForm几层组成。其中数据库使用的MySQL+MySQL.Data.dll Driver。不过在使用中,经常碰到如下错误:

接收对 http://localhost/***.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。有关详细信息,请参见服务器日志。

有时也会出现如下错误

基础连接已经关闭: 连接被意外关闭。

经过仔细核对发现,每当发生MySql.Data.MySqlClient.MySqlException异常时,就会报这个错误。网上找了半天也没有找到解决方案,后来使用Trace Viewer跟踪才发现是MySqlException序列化时出现了问题:

System.ServiceModel.CommunicationException: 尝试对参数 http://tempuri.org/ 进行序列化时出错: result。InnerException 消息是“不应为数据协定名称为“MySqlException:http://schemas.datacontract.org/2004/07/MySql.Data.MySqlClient”的类型“MySql.Data.MySqlClient.MySqlException”。请考虑使用 DataContractResolver,或将任何未知类型以静态方式添加到已知类型的列表。例如,可以使用 KnownTypeAttribute 特性,或者将未知类型添加到传递给 DataContractSerializer 的已知类型列表。”。有关详细信息,请参见 InnerException。  System.Runtime.Serialization.SerializationException:.......

其中result是一个存储操作结果的类实例,它横跨DAL、WCF、WinForm三层,用以存储一个完整操作中发现的异常及信息。里面对可能发生的异常做了包装,它的结构如下(其它属性及构造函数等没有列出):

[DataContract]

public class Result

{

    private bool success;

 

    [DataMember]

    public boll Success

    {

        get {return this.success;}

        set {this.success = value;}

    }

 

    private Exception exception;

    

    [DataMember]

    publice Exception Exception

    {

        get {return this.exception;}

        set {this.exception = value;}

    }

}

当DAL层中涉及SQL语句的执行发生错误时,result.Exception类型实际为MySqlException。虽然在Result类定义时已经声明[DataContract],不过序列化时仍然出错,头疼!查看MySqlException的定义如下:

[Serializable]

public sealed class MySqlException : DbException

{

    public int Number { get; }

}

明明是已经声明了[Serializable]特性,而且查看MySQL.Data.dll 文档,发现其在0.7版本的时候,就已经支持序列化了,却仍然不可在WCF中序列化,只能再找原因。

根据错误的提示,在IService的契约声明接口中,加上了[ServiceKnownType(typeof(MySql.Data.MySqlClient.MySqlException))]声明,可到头来结果还是一样不起作用。但是如果发生的异常不是MySqlException类型时,就不会报序列化错误。

对比Exception和MySqlException的实现,发现虽然都声明了[Serializable]特性,但MySqlException却没有实现序列化和逆序列化的函数。问题很可能出现在这个地方。

不过由于没有MySQL.Data.dll 的源代码,无法让添加这两个函数,只得新建一个异常类CustomException存放在Result。其代码如下:

[Serializable]    

public class CustomException : ISerializable,Exception

{

    private string message;

 

    public override string Message

    {

        get

        {

            return this.message;

        }

    }

 

    private string stackTrace;

 

    public override string StackTrace

    {

        get

        {

            return this.stackTrace;

        }

    }

 

    public CustomException() { }

 

    public CustomException(string exceptionMessage, string exceptionStackTrace)

    {

        this.message = exceptionMessage;

        this.stackTrace = exceptionStackTrace;

    }

    /*

     以下序列化和逆序列化中,只保存了Message和StackTrace

     */

    protected CustomException(SerializationInfo info, StreamingContext context)

    {

        this.message = info.GetString("Message");

        this.stackTrace = info.GetString("StackTraceString");

    }

 

    public override void GetObjectData(SerializationInfo info, StreamingContext context)

    {

        info.AddValue("Message", this.message);

        info.AddValue("StackTraceString", this.stackTrace);

    }

 

    public override string ToString()

    {

        if (string.IsNullOrEmpty(this.message)) 

            return string.Empty;

 

        return this.message.ToString();

    }

}

之所以override定义Message和StackTrace,是为能对它们俩重写,因为Exception类中,定义的StackTrace是只读的,Message也只能在构造函数中赋值。此外,此处只定义了这两个属性,是因为项目中只用到了Exception中的这两个属性,一般情况下,这两个属性也已经足够查找异常信息了。

当然最主要的地方,还是要实现一个构造函数,用来序列化,以及GetObjectData 方法设置序列化对象的替代值。

 

再次跟踪执行,当产生MySqlException时,Result对象可以正常从DAL->WCF->WinForm。OK啦!

 

总结:这个问题,的确是折磨了我好几天,由于是对WCF头一次在项目中使用,对它的了解也不多。如果不是看到使用Trace Viewer,还不会找到问题的本质所在。看来.NET下的异常提示,有时候也不一定就是问题的产生点,就像编译代码一样,少个逗号,提示的信息却是五花八门。

抱歉!评论已关闭.