none
Erro ao requisitar objetos via WCF. RRS feed

  • Pergunta

  • Estou com um problema muito peculiar usando WCF.
    Temos um serviço WCF que está utilizando Username Authentication com IPrincipal para política de acesso.
    Dentro desse serviço WCF possuimos vários métodos para listagem de objetos no banco, que atualmente estamos utilizando o Db4O, mas toda essa comunicação com o banco está na camada de negócio do sistema, abaixo do WCF.

    Quando tento recuperar uma estrutura de objetos do banco, ocorre um erro entre o retorno do mêtodo do serviço chamado e o retorno chegar no cliente, todo o restante funciona. Inclusive o servidor não entra em estado fault nem nada... o erro se concentra específicamente no retorno do mêtodo ao cliente. E se a excessão for tratada eu continuo efetuando normalmente as operações, quando não envolvendo a estrutura que causa a excessão.

    Eis a cadeia de Excessões disparadas:

    +        [System.ServiceModel.CommunicationException]    {"An error occurred while receiving the HTTP response to http://localhost:8731/Design_Time_Addresses/WorldWeb.Server.Services/Service/. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."}    System.ServiceModel.CommunicationException

    +        InnerException    {"The underlying connection was closed: An unexpected error occurred on a receive."}    System.Exception {System.Net.WebException}

    +        InnerException    {"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host."}    System.Exception {System.IO.IOException}

    +        InnerException    {"An existing connection was forcibly closed by the remote host"}    System.Exception {System.Net.Sockets.SocketException}

    +        InnerException    null    System.Exception

    Eis a estrutura em questão:

        [Serializable]
        [DataContract]
        public class Perfil
        {
            [DataMember]
            public string Nome { get; set; }
            [DataMember]
            public string Descricao { get; set; }
            [DataMember]
            public IList Funcionalidades { get; set; }

            public Perfil()
            {
                this.Nome = "";
                this.Descricao = "";
                this.Funcionalidades = new ArrayList();
            }
        }

    A IList Funcionalidades contêm objetos do tipo:

        [Serializable]
        [DataContract]
        public class Funcionalidade
        {
            [DataMember]
            public string Codigo { get; set; }
            [DataMember]
            public string Nome { get; set; }
            [DataMember]
            public string Descricao { get; set; }

            public Funcionalidade()
            {
                this.Codigo = "";
                this.Nome = "";
                this.Descricao = "";
            }
        }

    E a assinatura do método do serviço requisitado é:

        [ServiceContract]
        public interface IService
        {
            [OperationContract]
            Perfil getPerfil(string nome);
        }

    Pesquisando no google eu percebi que este erro aparece em inúmeras situações, porém não encontrei um caso parecido com este. Gostaria de entender o porquê do caso para evitar futuros problemas.

    []'s
    quinta-feira, 30 de julho de 2009 17:08

Respostas

Todas as Respostas

  • Boas wakedown,

    Já tentou definir o atributo includeExceptionDetailInFaults como True, para ver se ele te retorna maiores informações? Uma outra alternativa seria você habilitar o tracing e ver onde o problema ocorre.
    http://www.israelaece.com
    quinta-feira, 30 de julho de 2009 17:28
    Moderador
  • Boa tarde Israel, segundo conselhos eu mudei a classe Perfil para usar uma List<Funcionalidade> ao invez de uma IList, e funcionou. Pode ter algum problema associado ao uso do IList?

    Debugando para ver onde ocorre a excessão, todo o fluxo dentro do servidor ocorre sem falha alguma, o servidor nem nota a existência do erro. Nada aparece quando habilito o tracing.

    []'s
    quinta-feira, 30 de julho de 2009 18:10
  • Boas wakedown,

    Quando você retorna IList, a propriedade Funcionalidades no proxy será gerada como um Array de Objetos. Apesar de não ter tipagem aqui, ele deve funcionar sem problemas, ou seja, não acredito que isso seja a questão. Para melhor isso, use IList<Funcionalidade>, e deixe o resto para o WCF http://www.israelaece.com/post/Utilizando-Generics-em-Servicos.aspx

    É importante dizer também que o fato dele retornar um array do ancestral comum do .NET, talvez em algum lugar ele está dando problema devida a possíveis casts que eventual esteja fazendo.
    http://www.israelaece.com
    quinta-feira, 30 de julho de 2009 18:23
    Moderador
  • Então Israel,

    O engraçado dessa história é que outros objetos que utilizam o IList com outros objetos mais complexos funciona sem maiores problemas.

    Eu gostaria de entender o porquê desse problema, mas utilizando Generics funcionou, então vou adotar essa abordagem.

    []'s
    quinta-feira, 30 de julho de 2009 18:45
  • Boas Rafael,

    Copiando o seu código acima e colocando aqui, implementando o método getPerfil da seguinte:

    public Perfil getPerfil(string nome)
    {
        Perfil p = new Perfil() { Nome = nome };
        p.Funcionalidades.Add("Israel");

        return p;
    }

    funcionou sem maiores problemas.


    http://www.israelaece.com
    quinta-feira, 30 de julho de 2009 18:51
    Moderador
  • Israel,

    funcionou usando IList mesmo? :perplexo:

    []'s
    quinta-feira, 30 de julho de 2009 19:51
  • Boas Rafael,

    Sim, sem problemas:

    using (Servico.ServiceClient p = new ConsoleApplication2.Servico.ServiceClient())
        foreach (var item in p.getPerfil("IA").Funcionalidades)
            Console.WriteLine(item);
    http://www.israelaece.com
    quinta-feira, 30 de julho de 2009 19:56
    Moderador
  • Boa tarde Israel,

    é realmente estranho. O teu teste é o mais simples possível, certo? Não haveria alguma situação peculiar envolvendo a configuração do meu serviço?

    Abaixo segue o meu App.config do servidor:

    <configuration>
      <system.serviceModel>
        <bindings>
          <wsHttpBinding>
            <binding name="Binding">
              <security mode="Message">
                <message clientCredentialType="UserName" />
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior name="WorldWeb.Server.Services.ServiceBehavior">
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceCredentials>
                <userNameAuthentication userNamePasswordValidationMode="Custom" 
                                        customUserNamePasswordValidatorType="WorldWeb.Server.Services.Security.CustomValidator, WorldWeb.Server"/>
                <serviceCertificate findValue="tempClientcert" 
                                    x509FindType="FindBySubjectName" 
                                    storeName="My"/>
              </serviceCredentials>
              <serviceAuthorization principalPermissionMode="Custom">
                <authorizationPolicies>
                  <add policyType="WorldWeb.Server.Services.Security.AuthorizationPolicy, WorldWeb.Server"/>
                </authorizationPolicies>
              </serviceAuthorization>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <service behaviorConfiguration="WorldWeb.Server.Services.ServiceBehavior"
            name="WorldWeb.Server.Services.Service">
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding" contract="WorldWeb.Server.Services.IService">
              <identity>
                <dns value="tempClientcert" />
              </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8731/Design_Time_Addresses/WorldWeb.Server.Services/Service/" />
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>  
    </configuration>

    O serviço é inicializado conforme abaixo:

                ServiceHost host = null;
                try
                {
                    host = new ServiceHost(typeof(Service));
                    host.Open();
                    Console.WriteLine("Service running...");
                    Console.WriteLine(host.BaseAddresses.First());
                }
                catch (Exception e)
                {
                    Console.WriteLine("[wcf service] error: {0}", e.Message);
                    if (host != null)
                        host.Abort();
                }
                finally
                {
                    Console.ReadKey();
                    host.Close();
                }

    e eu abro a conexão com o cliente assim:

                    var factory = new ChannelFactory<ServiceRef.IService>("*");
                    factory.Credentials.UserName.UserName = "fumasa";
                    factory.Credentials.UserName.Password = "qwerty".createSHA256();
                    var proxy = factory.CreateChannel();

    []'s
    sexta-feira, 31 de julho de 2009 16:38
  • Boas Rafael,

    O exemplo que fiz foi semelhante ao teu em termos de contrato e funcionalidade, mas não em nível de configuração, como é o caso da autenticação/autorização. Utilizei também WSHttpBinding para a comunicação.
    http://www.israelaece.com
    sexta-feira, 31 de julho de 2009 18:47
    Moderador