Certificação Microsoft 70-487: Objetivo 1.5 – Create and implement a WCF Data Services service

A discussão sobre WCF Data Services no objetivo 1.1 foi curta porque há um objetivo específico para ele, o Create and implement a WCF Data Services service, que será coberto nesse post.

Esse objetivo cobre como endereçar recursos, criar uma query, acessar formatos de carga (incluindo JSON) e trabalhar com interceptores e operadores de serviço. Vamos lá?

Endereçando Recursos

Para criar um WCF Data Service, basicamente você deve seguir os seguintes passos:

  • Criar uma aplicação ASP.NET (para hostear o Data Service);
  • Usar o EF para criar seu EntityModel;
  • Adicionar um WCF Data Service;
  • Especificar um tipo para o serviço (que é o nome do model container do projeto do EF);
  • Habilitar acesso ao Data Service configurando explicitamente algumas propriedades do DataServiceConfiguration (SetEntitySetAccessRule, SetServiceOperationAccessRule e DataServiceBehavior)

Quando você adicionar um WCF Data Service, você verá classes geradas assim: public class ExamSampleService : DataService. Essas classes herdam de System.Data. Services.DataService, que recebem um tipo genérico. O código gerado terá também o método InitializeService com alguns comentários que ajudam na sua configuração:

// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config) {
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
// Examples:
// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
// config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}

Como já citado, é importante se familiarizar com os métodos SetEntitySetAccessRule e SetServiceOperationAccessRule e a propriedade DataServiceBehavior.MaxProtocolVersion.

SetEntitySetAccessRule

Esse método recebe dois parâmetros: uma string com o nome do EntitySet e um enum EntitySetRights. Como o enum é decorado com Flags, você pode passar mais de um valor dessa forma: config.SetEntitySetAccessRule("Courses", EntitySetRights.AllRead | EntitySetRights.WriteMerge);. Os possíveis valores desse enum são:

  • None: todos os acessos são revogados;
  • ReadSingle: um item pode ser lido;
  • ReadMultiple: sets de dados podem ser lidos;
  • WriteAppend: novos itens podem ser adicionados;
  • WriteReplace: dados podem ser atualizados;
  • WriteDelete: dados podem ser deletados;
  • WriteMerge: dados podem ser mergeados;
  • AllRead: tudo pode ser lido;
  • AllWrite: tudo pode ser escrito;
  • All: todas as operações são permitidas.

SetServiceOperationAccessRule

Esse método dá permissões para uma ServiceOperation e funciona da mesma forma que o método SetEntitySetAccessRule, mas o enum é um ServiceOperationRights. Os valores desse enum são:

  • None: todos os acessos são revogados;
  • ReadSingle: um item pode ser lido;
  • ReadMultiple: sets de dados podem ser lidos;
  • AllRead: um e vários itens podem ser lidos;
  • All: todos os direitos são garantidos;
  • OverrideEntitySetRights: indica se sobrescreve os valores configurados em SetEntitySetAccessRule.

DataServiceBehavior.MaxProtocolVersion

Essa propriedade indica qual versão do protocolo OData será usado no serviço. Os possíveis valores do enum DataServiceProtocolVersion são V1 e V2.

Criando Uma Query

Uma das principais vantagens do WCF Data Services é que seus recursos são endereçáveis por URIs via OData, e você pode fazer queries nas URIs de uma forma bem simples.

Suponha que você criou um WCF Data Service com o nome ExamPrepService baseado num Entity Model que tem tópicos, questões e respostas como entidades.

Para obter todos os registros de uma entidade, a query Service/EntitySetName pode ser usada. Para obter todos os tópicos do serviço, por exemplo, a seguinte query pode ser usada:
http://servicehost/ExamPrepService.svc/Topics

A query Service/EntitySetName(‘KeyValue’) retorna a entidade que tem uma key com o valor passado. Por exemplo, para obter o tópico que tem o nome First, a query fica http://servicehost/ExamPrepService.svc/Topics('First').

Se você precisa somente da propriedade Description, a query fica http://servicehost/ExamPrepService.svc/Topics('First')/Description. Se quiser somente o valor primitivo e não o XML, basta adicionar /$value no final: http://servicehost/ExamPrepService.svc/Topics('First')/Description/$value.

As queries funcionam bem com relacionamentos, se esses estiverem definidos no Entity Model. Para obter todas as Answers da Question com a key 1, por exemplo, a seguinte query deve ser usada:
http://servicehost/ExamPrepService.svc/Questions('1')/Answers

O caminho inverso também é possível: http://servicehost/ExamPrepService.svc/Answers('1:4')/Question.

Até agora, só filtramos os resultados pela key da entidade. Mas o protocolo OData e o WCF Data Service oferecem várias opções de query que se traduzem em queries SQL. É bem importante entender o efeito de todos eles no resultado.

  • $orderby: define uma ordem para o resultado. Uma ou mais propriedades podem ser usadas, separadas por vírgula: /Questions?$orderby=Id,Description;
  • $top: define quantos resultados serão retornados: /Questions?$top=5
  • $skip: indica quantos registros devem ser ignorados. Suponha que há 30 tópicos, mas você quer ignorar os 10 primeiros e os 10 últimos. A query fica assim: /Questions?$skip=10&$top=10;
  • $filter: especifica uma ou mais condições: /Questions?$filter=Id gt 5;
  • $expand: indica quais entidades relacionadas serão retornadas: /Questions?$expand=Answers;
  • $select: por padrão, todas as propriedades de uma entidade são retornadas. Usando $select, você pode especificar quais propriedades serão retornadas separando por vírgula: /Questions&$select=Id,Text,Description,Author;
  • $inlinecount: retorna o número de registros retornados no elemento <count>: /Questions?$inlinecount=allpages.

Você também pode executar queries por meio de código usando a classe DataServiceQuery:

String ServiceUri = "http://servicehost/ExamPrepService.svc";
ExamServiceContext ExamContext = new ExamServiceContext(new Uri(ServiceUri); DataServiceQuery = ExamContext.Question
.AddQueryOptions("$filter", "id gt 5")
.AddQueryOptions("$expand", "Answers");

Acessando Formatos De Carga

o WCF Data Service dá suporte aos formatos Atom e JSON de forma fácil.

Basta usar a opção $format na query com o valor atom ou json. Caso esteja consultando via WebClient ou queira especificar isso em um request header, funciona da mesma maneira. Para JSON, coloque o header Content-Type para application/json. Para Atom, application/atom+xml.

Trabalhando Com Interceptor e Service Operator

O WCF Data Service permite que você intercepte um request para prover uma lógica customizada.

Há dois tipos de Interceptors: ChangeInterceptor, que serve para interceptar operações NON-Query, e QueryInterceptor, que intercepta operações de Query.

ChangeInterceptors têm retorno nulo e aceitam dois parâmetros: o tipo da entidade e um UpdateOperations, que referencia o request. A definição de um método ChangeInterceptor fica assim:

[ChangeInterceptor("Topics")]
public void OnChangeTopics(Topic topic, UpdateOperations operations)

QueryInterceptors são decorados com QueryInterceptor e retornam uma Expression>, que servem como filtro para a entidade. A assinatura fica assim:

[QueryInterceptor("Topics")]
public Expression> FilterTopics(){}

A dica para o exame é que, para identificar se um método é ChangeInterceptor ou QueryInterceptor, basta olhar para o tipo do retorno.
Para saber qual Interceptor deve ser implementado para fazer alguma coisa, é necessário saber o comportamento e determinar se é uma operação NON-Query ou Query. Por exemplo, DeleteTopics seria um ChangeInterceptor; já um método GetAdvancedTopics seria um QueryInterceptor.

Qualquer dúvida, fique à vontade para fazê-la nos comentários.
Fique ligado para o post do próximo objetivo, Manipulate XML data structures.

Até mais!