Parabéns à moving2u!

Faz hoje 5 anos, nascia em Coimbra a moving2u, empresa da qual sou sócio desde a sua criação. Tal como o seu nome deixa adivinhar, a empresa nasceu com o objectivo de desenvolver e comercializar soluções de mobilidade para profissionais, objectivo ao qual se manteve inteiramente fiel ao longo destes 5 anos, e pelo qual é cada vez mais reconhecida. Nestes 5 anos conquistámos importantes clientes em diversas àreas de negócio, estabelecemos parcerias com várias empresas responsáveis pela implementação, distribuição e desenvolvimento de software de gestão/ERPs, e vimos reconhecido o nosso trabalho por algumas empresas de referência, como a Motorola/Symbol ou a Microsoft, da qual somos ‘Certified Partner – Mobility Solutions’, e muito importante, criámos laços de amizade com muitas pessoas.


Desde o primeiro momento que decidimos apostar na plataforma Windows Mobile/CE, quando os equipamentos com este sistema operativo ainda não se tinham afirmado no mercado profissional em Portugal, estratégia que se revelou compensadora pois hoje a plataforma Windows Mobile conjugada com as ferramentas de desenvolvimento e outras tecnologias da Microsoft é de longe a escolha mais ‘produtiva’ e que melhores aplicações permite desenvolver para terminais móveis.


Se é do desenvolvimento das minhas funções – responsável de R&D – que ‘nascem’ muitas das experiências que partilho neste blog, nos fórums ou nas sessões que apresento, também é por causa das mesmas funções que a minha disponibilidade para manter o blog actualizado ou propor-me a outras iniciativas tem vindo a ser muito reduzida, esperando-se que o volume de trabalho se mantenha ao longo dos próximos tempos.


Saúde e longa vida à moving2u, seus colaboradores e amigos!!!

Parser para CFFaultExceptions

Estou envolvido num projecto assente em .NET CF 3.5, onde utilizamos WCF sobre HTTP para recuperar informação de um servidor.

Se é verdade que a .NET CF 3.5 traz suporte a este novo modelo de comunicações, esse suporte é limitado, como podemos ver neste post do Andrew Arnott. Sendo suportado, quem já utilizou o WCF sobre HTTP para .NET CF, já se terá apercebido que esse suporte não é totalmente ‘nativo’, pois ao contrário do que acontece na full framework, para além das classes proxy, é sempre criado um ficheiro chamado CFClientBase.cs ou .vb, onde se baseia muito do suporte a WCF sobre HTTP pela .NET CF.

A limitação desse suporte chega ao ponto das FaultExcpetions, neste caso, CFFaultException, não serem expostas detalhadas. Se por exemplo tivermos uma chamada a um serviço dentro de um bloco Try/Catch, e este despoletar uma CFFaultException:

Try
    ...
Catch ex as CFFaultException
    MsgBox(ex.Message)
End Try


O que obtemos na message box, é uma mensagem <not> muito </not> explícita: “CFFaultException”.


Ao inspeccionar o objecto ex, chegamos a uma propriedade FaultString, do tipo string, onde encontramos um documento XML bem formado, e lá no meio, encontramos a causa da excepção. Esta inspecção visual até serve em ambiente de desenvolvimento, mas é importante poder recuperar essa informação programaticamente, pelo que se impõe um parser para este XML. Uma pesquisa com o Google a CFFaultException parser não produz resultados, colocando apenas CFFaultException, lá aparece uma página em italiano de um MVP, Andrea Boschin. Os meus conhecimentos da língua da Monica Bellucci são muito limitados – da língua da Monica em si, nulos mesmo – mas lá consegui chegar à conclusão que o tópico era o que me interessava – neste caso, a CFFaultException, não a Monica – ou seja, um parser para a CFFaultException em C#. Como este projecto está a ser desenvolvido em VB.net, recorri ao meu tradutor preferido de C# para VB.net, e completei o serviço corrigindo um ou outro erro:

Imports System.Text
Imports System.Xml.Serialization
Imports System.IO
Imports System.ServiceModel
    <XmlRoot("Fault", Namespace:="http://schemas.xmlsoap.org/soap/envelope/")> _
Public Class ServiceFault
        Private _faultCode, _faultString As String
        Private _detail As List(Of ServiceFaultDetail)
        Public Shared Empty As ServiceFault = New ServiceFault
        Public Sub New()
            MyBase.New()
            Me.Detail = New List(Of ServiceFaultDetail)
        End Sub
        <XmlElement("faultcode", Namespace:="")> _
        Public Property FaultCode() As String
            Get
                Return _faultCode
            End Get
            Set(ByVal value As String)
                _faultCode = value
            End Set
        End Property
        <XmlElement("faultstring", Namespace:="")> _
        Public Property FaultString() As String
            Get
                Return _faultString
            End Get
            Set(ByVal value As String)
                _faultString = value
            End Set
        End Property
        <XmlArray("detail", Namespace:=""), _
         XmlArrayItem(GetType(ServiceFaultDetail), Namespace:="http://schemas.datacontract.org/2004/07/System.ServiceModel")> _
        Public Property Detail() As List(Of ServiceFaultDetail)
            Get
                Return _detail
            End Get
            Set(ByVal value As List(Of ServiceFaultDetail))
                _detail = value
            End Set
        End Property
    End Class
    <XmlType(TypeName:="ExceptionDetail")> _
    Public Class ServiceFaultDetail
        Private _message, _stackTrace, _type As String
        Public Sub New()
            MyBase.New()
        End Sub
        <XmlElement("Message", Namespace:="http://schemas.datacontract.org/2004/07/System.ServiceModel")> _
        Public Property Message() As String
            Get
                Return _message
            End Get
            Set(ByVal value As String)
                _message = value
            End Set
        End Property
        <XmlElement("StackTrace", Namespace:="http://schemas.datacontract.org/2004/07/System.ServiceModel")> _
        Public Property StackTrace() As String
            Get
                Return _stackTrace
            End Get
            Set(ByVal value As String)
                _stackTrace = value
            End Set
        End Property
        <XmlElement("Type", Namespace:="http://schemas.datacontract.org/2004/07/System.ServiceModel")> _
        Public Property Type() As String
            Get
                Return _type
            End Get
            Set(ByVal value As String)
                _type = value
            End Set
        End Property
    End Class
    Public Class FaultSerializer
        ''' <summary>
        ''' Deserializes the specified fault.
        ''' </summary>
        ''' <param name="fault">The fault.</param>
        ''' <returns></returns>
        Public Shared Function Deserialize(ByVal fault As CFFaultException) As ServiceFault
            If (fault Is Nothing) Then
                Throw New ArgumentNullException("fault", "fault cannot be null")
            End If
            If String.IsNullOrEmpty(fault.FaultMessage) Then
                Return ServiceFault.Empty
            End If
            Dim xml As String = fault.FaultMessage
            Dim reader As StringReader = New StringReader(xml)
            Dim serializer As XmlSerializer = New XmlSerializer(GetType(ServiceFault), "http://schemas.xmlsoap.org/soap/envelope/")
            Return CType(serializer.Deserialize(reader), ServiceFault)
        End Function
        ''' <summary>
        ''' Serializes the specified fault.
        ''' </summary>
        ''' <param name="fault">The fault.</param>
        ''' <returns></returns>
        Public Shared Function Serialize(ByVal fault As ServiceFault) As String
            Dim builder As StringBuilder = New StringBuilder
            Dim writer As StringWriter = New StringWriter(builder)
            Dim ns As XmlSerializerNamespaces = New XmlSerializerNamespaces
            ns.Add("s", "http://schemas.xmlsoap.org/soap/envelope/")
            ns.Add("i", "http://www.w3.org/2001/XMLSchema-instance")
            ns.Add("a", "http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher")
            Dim serializer As XmlSerializer = New XmlSerializer(fault.GetType, "http://schemas.xmlsoap.org/soap/envelope/")
            serializer.Serialize(writer, fault, ns)
            Return builder.ToString
        End Function
    End Class

Assim, podemos mudar o nosso exemplo para mostrar, por ex., a propriedade .Message associada à excepção:

Try
    ...
Catch ex as CFFaultException
    MsgBox(FaultSerializer.Deserialize(ex).Detail(0).Message)
End Try

Os agradecimentos vão para o Andrea Boshcin!


 


[Actualização]


O seguinte extension method permite obter de imediato a mensagem associada à excepção:

        <Extension()> _
        Public Function ExceptionMessage(ByVal exception As CFFaultException) As String
            Return FaultSerializer.Deserialize(exception).FaultString
        End Function
        ...
        Try
            ...
        Catch ex as CFFaultException
            MsgBox(ex.ExceptionMessage)
        End Try