Desenvolvimento - C#

WCF - Herança de Objetos com KnownTypes

Esse artigo descreve como implementar herança em classes DataContract no WCF usando o atributo KnownTypes.

por Pedro Henrique Barbosa Fernandes



É comum em aplicações orientadas a objetos, usarmos classes que são classes derivadas de outras classes base, como por exemplo, uma classe Carro e uma classe Moto que possuem como classe base a classe Veiculo. Baseando neste exemplo, imagine um método chamado CalculaParcela que recebe como parâmetro um objeto do tipo Veiculo:

public decimal CalculaParcela(Veiculo Veiculo) { …..

Uma das grandes vantagens de usar classes base é que, o parâmetro do tipo base consequentemente aceita todos os tipos derivados, assim um parâmetro do tipo Veiculo, irá aceitar objetos do tipo Carro e Moto.

Infelizmente com serviços fornecidos com WCF não acontece da mesma forma. Entretanto através do atributo KnownType podemos contornar este problema, dizendo para a classe base quais tipos derivados aquela classe irá suportar. Vamos entender melhor analisando os códigos abaixo:

Classe base Veiculo, note que usamos o atributo KnownType para dizermos que todo parâmetro do tipo Veiculo, também irá aceitar o tipo Carro:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Runtime.Serialization;

namespace WCFService.DataContracts

{

    [DataContract]

    [KnownType(typeof(Carro))]

    public class Veiculo

    {

        [DataMember]

        public string Nome { get; set; }

        [DataMember]

        public decimal Preco { get; set; }

        [DataMember]

        public string Fabricante { get; set; }

        public virtual decimal CalculaPrecoParcela(int NumParcelas)

        {

            return (Preco + (Preco / 100) * NumParcelas) / NumParcelas;

        }

    }

}

Criamos um método na classe Veiculo que recebe o número de parcelas e calcula o preço de cada parcela aumentando em 1% para cada parcela, assim um carro de R$ 10000,00 parcelado em dez vezes irá aumentar 10% do total, resultando um valor de R$ 1100,00 por parcela.

Agora temos a classe Carro que deriva da classe Veiculo e sobrepõe o método CalculaPrecoParcela(), aumentando o valor em 1,5% por parcela em vez de 1% como foi definido na classe Veiculo:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Runtime.Serialization;

namespace WCFService.DataContracts

{

    [DataContract]

    public class Carro : Veiculo

    {

        public override decimal CalculaPrecoParcela(int NumParcelas)

        {

            return (Preco + (Preco / 100) * (NumParcelas * (decimal)1.5)) / NumParcelas;

        }

    }

}

Definindo o contrato:

using System.ServiceModel;

using WCFService.DataContracts;

namespace WCFService.ServiceContracts

{

    [ServiceContract]

    public interface IVeiculoService

    {

        [OperationContract]

        decimal CalculaParcela(Veiculo Veiculo, int NumParcelas);

    }

}

E agora o serviço:

using WCFService.ServiceContracts;

using WCFService.DataContracts;

namespace WCFService.Implementation

{

    public class VeiculoServiceImpl : IVeiculoService

    {

        public decimal CalculaParcela(Veiculo Veiculo, int NumParcelas)

        {

            return Veiculo.CalculaPrecoParcela(NumParcelas);

        }

    }

}

Podemos agora chamar o serviço e passar para a operação CalculaParcela() objetos do tipo Veiculo ou Carro:

using System;

using WCFClient.VeiculoServiceClient;

namespace WCFClient

{

    class Program

    {

        static void Main(string[] args)

        {

            var Carro = new Carro

            {

                Preco = 100000,

                Nome = "Uno",

                Fabricante = "Fiat"

            };

            var Veiculo = new Veiculo

            {

                Fabricante = "Mercedes",

                Nome = "Ônibus",

                Preco = 100000

            };

            Console.WriteLine("=== Testando o KnownTypes ===");

            using (var client = new VeiculoServiceClient.VeiculoServiceClient())

            {

                Console.WriteLine("Carro = {0:C}", client.CalculaParcela(Carro, 10));

                Console.WriteLine("Ônibus = {0:C}", client.CalculaParcela(Veiculo, 10));

            }

            Console.ReadKey();

        }

    }

}

Com esse simples recurso podemos usar no cliente que vai consumir o serviço o mesmo modelo de classes que há no servidor, planejando e definindo os tipos bases e derivados permitindo assim uma maior interação entre cliente e servidor.

Pedro Henrique Barbosa Fernandes

Pedro Henrique Barbosa Fernandes