none
Controle massa de dados no retorno de metodo RRS feed

  • Pergunta

  • Olá galera,

    A duvida agora é um pouco mais avançada..mais vamos lá..

    tenho um metodo no servico que me retorna uma exception personalizada...

    nessa exception desejo adicionar uma propriedade contendo uns dos integrantes do processo.. ou seja, desta forma consigo ver qual objeto que deu problema e com isso consigo alterar algumas coisas na tela... exemplo..

    public class ProcessarElementos()
    {
         foreach (Elemento e in elementos)
         {
              if (!elemento.Verificar())
              {
                      new throw MinhaException(elemento);
               }
         }
    }

    vejam que a minha exception recebo o elemento...

    na apresentação (silverlight) irei receber essa exception, localizar o elemento na tela e mudar sua cor para vermelho...

    Bom, até aqui td certo... o problema foi o tamanho do pacote da excpetion...

    essa classe Elemento tem varios atributos, varias coleções e por ai vai... e quando ele  serializa a exception todos esses caras vão juntos serialiados... MAIS eu não preciso nem da metade desses atributos... apenas preciso de 5 propriedades da classe Elemento dentro de excpetion para localizar meu elemento na tela... ai pensei se haveria uma forma de retirar esses atributos APENAS quando o WCF serializar minha Exception, em outros metodos eles viriam normalmente...

    vou citar um exemplo totalmente ABSTRATO do q preciso:

    [DataContract]
    class MinhaException : Exception
    {
        [DataMember]
        [IgnorarPropriedadesDeElemento(Nome="Listas1")]
        [IgnorarPropriedadesDeElemento(Nome="Listas2")]
        [IgnorarPropriedadesDeElemento(Nome="Propriedade1")]
        [IgnorarPropriedadesDeElemento(Nome="Propriedade2")]

    // OU

        // Aqui eu carrego apenas a Propriedade1 e apenas suas propriedades d primeiro nivel, tipo Elemento.Propriedade1.Nome
        // Elemento.Propriedade1.OutroElemento: naõ seria serializavel por exemplo
        [CarregarApenasPrimeiroNivel(Nome="Propriedade1")]
        public Elemento elemento {get;set;}
    }

    tem jeito de resolver esse meu problema com WCF?? caso não tenha, vcs teriam uma sugestão para dar de como vcs resolvem esses tipos de problema?

    vlw galera
    Ozzyvegeta
    quinta-feira, 3 de setembro de 2009 19:05

Respostas

  • Boas Ozzyvegeta,

    Bem, estava fazendo alguns testes aqui, e fiz um exemplo que aparentemente funcionou, ou seja, ao ocorrer a exceção, eu estou limpando as propriedades que estão decoradas com um atributo personalizado que criei. Talvez você possa fazer mais algumas configurações para ajustar a sua necessidade.

    namespace Host
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (ServiceHost host = new ServiceHost(typeof(Servico), new Uri[] { new Uri("net.tcp://localhost:9393") }))
                {
                    host.Description.Behaviors.Add(new ServiceMetadataBehavior());

                    host.AddServiceEndpoint(typeof(IContrato), new NetTcpBinding(), "srv");
                    host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");

                    host.Open();
                    Console.ReadLine();
                }
            }
        }

        [DataContract]
        internal class ClasseComplexa
        {
            [DataMember]
            public string Propriedade1 { get; set; }

            [DataMember]
            public int Propriedade2 { get; set; }

            [IgnoreOnError]
            [DataMember]
            public string[] Propriedade3 { get; set; }
        }

        [ServiceContract]
        internal interface IContrato
        {
            [OperationContract]
            [FaultContract(typeof(DetalhesDoErro<ClasseComplexa>))]
            ClasseComplexa[] RecuperarClasses();
        }

        internal class Servico : IContrato
        {
            public ClasseComplexa[] RecuperarClasses()
            {
                List<ClasseComplexa> temp = CarregarDados();

                foreach (var item in temp)
                    if (!item.Validar())
                        throw new FaultException<DetalhesDoErro<ClasseComplexa>>(new DetalhesDoErro<ClasseComplexa>(item));

                return temp.ToArray();
            }

            private List<ClasseComplexa> CarregarDados() //Dummy
            {
                List<ClasseComplexa> list = new List<ClasseComplexa>();

                list.Add(
                    new ClasseComplexa()
                    {
                        Propriedade1 = "Israel",
                        Propriedade2 = 2,
                        Propriedade3 = new List<string>(new string[] { "v1", "v2" }).ToArray()
                    });

                return list;
            }
        }

        [DataContract(Name = "DetalhesDoErro")]
        internal class DetalhesDoErro<TDetail>
        {
            [DataMember]
            public TDetail ObjectInstance { get; set; }

            public DetalhesDoErro(TDetail detail)
            {
                this.ObjectInstance = detail;
                RemoveExtraData();
            }

            private void RemoveExtraData()
            {
                Type detailType = typeof(TDetail);

                foreach (PropertyInfo pi in detailType.GetProperties())
                    if (pi.GetCustomAttributes(typeof(IgnoreOnErrorAttribute), true).Length > 0)
                        pi.SetValue(this.ObjectInstance, null, null);
            }
        }

        internal class IgnoreOnErrorAttribute : Attribute { }
    }


    E do lado do cliente, você envolve a chamada para o método em um bloco de tratamento de erro, e captura a FaultException<T> que irá expor o objeto com o problema, como mostro abaixo:

    using (Servico.ContratoClient p = new Client.Servico.ContratoClient())
    {
        try
        {
            Servico.ClasseComplexa[] dados = p.RecuperarClasses();
        }
        catch (FaultException<Servico.DetalhesDoErro> ex)
        {
            Console.WriteLine(ex.Detail.ObjectInstance.Propriedade3 == null); //deve retornar true
        }
    }


    http://www.israelaece.com
    sexta-feira, 4 de setembro de 2009 02:10
    Moderador

Todas as Respostas

  • Boas Ozzyvegeta,

    Ao meu ver, acho que o mais ideal seria criar um segundo elemento somente com o que você precisa para representar a falha do lado do cliente, sendo algo mais ou menos como:

    [DataContract]
    public class ElementoComErro
    {
        public string Propriedade1 { get; set; }
        public string Propriedade2 { get; set; }

        public ElementoComErro(Elemento elemento)
        {
            this.Propriedade1 = elemento.Propriedade1;
            this.Propriedade2 = elemento.Propriedade2;
        }
    }

    E quando for disparar, faz:

    throw new MinhaException(new ElementoComErro(elemento));

    Dessa forma, você mantém um objeto exclusivo com o que ele representa, enquanto possui um segundo objeto apenas com as propriedades necessárias para representar a falha do lado do cliente.
    http://www.israelaece.com
    quinta-feira, 3 de setembro de 2009 19:12
    Moderador
  • certo, bem interessante...

    mais esse exemplo criei para demostrar esse tipo de ambiente.. nesse caso da pra contornar, mais e caso eu tenha um objeto que vai para o servidor, o servidor processa e retorna um tipo de classe relativamente pequeno mais que contem uma propriedade com grande peso. Exemplo...

    ClassePai, ClasseFilha..

    ClassePai tem uma coleção de Classes filhas, e irei retornar uma classe filha que é bem pequena... nesse caso ela ira serializar classe pai e todos os seus filhos juntos sendo que deles eu não necessito...

    bom, a questão seria, tem como fazer algo parecido com oq disse em meu primeiro post?? c naum tiver vou apelar criando uma classe filha somento com as propriedades que desejo e fazer um new ClassePai populando apenas as propriedades que desjo tb...

    mais acharia interessante se holvesse pois me daria mais flexibildade  do q serializar...

    vlw

    Ozzyvegeta
    quinta-feira, 3 de setembro de 2009 19:28
  • Boas Ozzyvegeta,

    Até onde eu sei, uma vez que você determina quais propriedades serão definidas como DataMember, incondicionalmente elas serão sempre enviadas ao cliente, pois isso esta "firmado em contrato".

    Uma alternativa seria você criar um serializador próprio, que olharia para esse atributo customizado que você viesse a criar, ou seja, excluir essa valor ao detectar que é uma fault. O problema é que criar um serializador é bem complexo. Ou ainda, tentar adaptar os Message Contracts: http://www.israelaece.com/post/WCF-MessageContracts.aspx.

    Ou, finalmente, talvez uma melhor solução seria implementar a Interface IXmlSerializable e customizar de acordo com o valor de uma propriedade (Error =true) e lá colocar somente as informações que deseja mandar, definindo o resto como vazio/null.
    http://www.israelaece.com
    quinta-feira, 3 de setembro de 2009 20:07
    Moderador
  • Bom, veja se entendi bem..

    esses tal de Message Contract alteram APENAS o formato da mensagem (xml) do SOAP, correto??

    mais percebi que APENAS essa mudança ocorre, caso eu utilize os atributos padrao ele irá serializar do mesmo jeito no cliente... ou seja, quall seria a finalidade dessas mensagens?? Tem exemplos?

    eu vi apenas um exemplo, me corrija se estiver errado: Um cliente que usa Vb6 quer manter um determinado formato XML e altera o retorno o formato do soap para isso... ta certo??

    e voltando ao meu problema, no oq esses message contract me ajudaria?

    Obrigado




    Ozzyvegeta
    quinta-feira, 3 de setembro de 2009 21:17
  • Boas Ozzyvegeta,

    Bem, estava fazendo alguns testes aqui, e fiz um exemplo que aparentemente funcionou, ou seja, ao ocorrer a exceção, eu estou limpando as propriedades que estão decoradas com um atributo personalizado que criei. Talvez você possa fazer mais algumas configurações para ajustar a sua necessidade.

    namespace Host
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (ServiceHost host = new ServiceHost(typeof(Servico), new Uri[] { new Uri("net.tcp://localhost:9393") }))
                {
                    host.Description.Behaviors.Add(new ServiceMetadataBehavior());

                    host.AddServiceEndpoint(typeof(IContrato), new NetTcpBinding(), "srv");
                    host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");

                    host.Open();
                    Console.ReadLine();
                }
            }
        }

        [DataContract]
        internal class ClasseComplexa
        {
            [DataMember]
            public string Propriedade1 { get; set; }

            [DataMember]
            public int Propriedade2 { get; set; }

            [IgnoreOnError]
            [DataMember]
            public string[] Propriedade3 { get; set; }
        }

        [ServiceContract]
        internal interface IContrato
        {
            [OperationContract]
            [FaultContract(typeof(DetalhesDoErro<ClasseComplexa>))]
            ClasseComplexa[] RecuperarClasses();
        }

        internal class Servico : IContrato
        {
            public ClasseComplexa[] RecuperarClasses()
            {
                List<ClasseComplexa> temp = CarregarDados();

                foreach (var item in temp)
                    if (!item.Validar())
                        throw new FaultException<DetalhesDoErro<ClasseComplexa>>(new DetalhesDoErro<ClasseComplexa>(item));

                return temp.ToArray();
            }

            private List<ClasseComplexa> CarregarDados() //Dummy
            {
                List<ClasseComplexa> list = new List<ClasseComplexa>();

                list.Add(
                    new ClasseComplexa()
                    {
                        Propriedade1 = "Israel",
                        Propriedade2 = 2,
                        Propriedade3 = new List<string>(new string[] { "v1", "v2" }).ToArray()
                    });

                return list;
            }
        }

        [DataContract(Name = "DetalhesDoErro")]
        internal class DetalhesDoErro<TDetail>
        {
            [DataMember]
            public TDetail ObjectInstance { get; set; }

            public DetalhesDoErro(TDetail detail)
            {
                this.ObjectInstance = detail;
                RemoveExtraData();
            }

            private void RemoveExtraData()
            {
                Type detailType = typeof(TDetail);

                foreach (PropertyInfo pi in detailType.GetProperties())
                    if (pi.GetCustomAttributes(typeof(IgnoreOnErrorAttribute), true).Length > 0)
                        pi.SetValue(this.ObjectInstance, null, null);
            }
        }

        internal class IgnoreOnErrorAttribute : Attribute { }
    }


    E do lado do cliente, você envolve a chamada para o método em um bloco de tratamento de erro, e captura a FaultException<T> que irá expor o objeto com o problema, como mostro abaixo:

    using (Servico.ContratoClient p = new Client.Servico.ContratoClient())
    {
        try
        {
            Servico.ClasseComplexa[] dados = p.RecuperarClasses();
        }
        catch (FaultException<Servico.DetalhesDoErro> ex)
        {
            Console.WriteLine(ex.Detail.ObjectInstance.Propriedade3 == null); //deve retornar true
        }
    }


    http://www.israelaece.com
    sexta-feira, 4 de setembro de 2009 02:10
    Moderador