Resolvendo o problema de Cross-Domain no Asp.Net WebApi

Olá pessoa!

Aqui vai uma dica rápida mas que ajuda bastante quem está desenvolvendo Api’s com o Asp.Net WebApi.

O Cross-Domain é o bloqueio de transações entre domínios diferentes, isto é, caso você crie uma api e hospede no ServidorA por exemplo, quando um determinado ServidorB for acessar via Ajax, pode receber a seguinte mensagem:

XMLHttpRequest cannot load http://servidos/api/metodo. Origin null is not allowed by Access-Control-Allow-Origin.

Basicamente, o que temos que fazer é informar no Header que está permitido o acesso proveniente de outro domínio: Access-Control-Allow-Origin.

Para isso, vamos criar um ActionFilter.

Um ActionFilter é uma forma de interceptar uma Action em um determinado Controller disparando eventos antes ou depois de sua execução.

Saiba mais aqui.

    public class EnableCrossDomainAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        const string Origin = "Origin";
        const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";

        public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
        {
            if (actionExecutedContext.Request.Headers.Contains(Origin))
            {
                string originHeader = actionExecutedContext.Request.Headers.GetValues(Origin).FirstOrDefault();
                if (!string.IsNullOrEmpty(originHeader))
                {
                    actionExecutedContext.Response.Headers.Add(AccessControlAllowOrigin, originHeader);
                }
            }
        }
    }

Essa classe pode ser salva no lugar que você achar conveniente. Geralmente eu crio uma pasta chamada Filters para esse tipo de classe.

Agora, basta adicionar esse filtro na classe ou no método que você quiser liberar o Cross-Domain.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApiOData.Controllers
{
    [EnableCrossDomain]
    public class BandasController : ApiController
    {
        [HttpGet]
        public IQueryable<Models.Banda> Listar()
        {
            Models.Repositorio repositorio = new Models.Repositorio();
            var bandas = repositorio.Listar();

            return bandas;
        }
    }
}

O Controller acima foi tirado do post onde eu falo sobre Asp.Net WebApi e OData. Veja!!

Agora é só testar!!

Resolveu??

É nóis!!

Muito Obrigado e até a próxima.

Angelo Belchior

Trilha Sonora do Post:

Peter Frampton : Breaking All The Rules

Desenvolvendo Aplicações para o Firefox OS

E ae pessoal, blz?

Pra quem não sabe, a nossa querida Mozilla criou um Sistema Operacional para dispositivos móveis: o glorioso Firefox OS.

http://www.mozilla.org/en-US/firefoxos/

E ninguém menos do que a Telefónica está apoiando esse projeto: http://www.openwebdevice.com/!!!

E o que eu acho mais empolgante é o fato de se poder usar HTML5+JS+CSS3 para desenvolver Aplicativos..!!

Anote ai: Todo e qualquer desenvolvedor Web que se preze tem que conhecer esse trio. Simples assim!

Mão à massa.

Bom, pra começar temos que baixar o emulador do SO que nada mais é do que um complemento para o Firefox.
Se você não tem o firefox clique aqui e baixe-o!

Baixou?? Agora abra o navegador, vá em Complementos.

Complementos

Na pesquisa digite Firefox OS. E clique em instalar.

Instalando

Depois de instalado a janela do emulador aparecerá:

pagina_emulador

E clicando no botão Stopped…

emulador

Maravilha!!

Caso queira acessá-lo pelo menu, basta ir em Firefox (botão laranja) -> Desenvolvedor Web -> Firefox OS Simulator.

Recomendo que você dê uma passeada pelo emulador… eu particularmente achei muito legal!!

Agora vamos ao que interessa!!

Como foi dito a cima, usaremos HTML5+JS+CSS3, sendo assim, fique a vontade para escolher sua IDE.

Eu vou usar o Notepad++… já tô acostumado com ele!!!

Vamos criar apenas três arquivos. manifest.webapp, index.html e um tal de jquery.js.

O arquivo manifest.webapp nada mais é do que um arquivo json. Nele indicamos algumas informações como Nome da Aplicação, Descrição, Versão, Caminho e tamanho dos Ícones, etc.

É um arquivo bem intuitivo, saca só:

{
  "version": "0.1",
  "name": "MeuPrimeiroApp",
  "description": "Meu Primeiro App",
  "launch_path": "index.html",
  "icons": {
    "16": "16x16.png",
    "48": "48x48.png",
    "128": "128x128.png"
  },
  "developer": {
    "name": "Angelo",
    "url": "http://pontonetbrasil.wordpress.com"
  },
  "locales": {
    "es": {
      "description": "Em espanhol!",
      "developer": {
        "url": "http://pontonetbrasil.wordpress.com"
      }
    },
    "it": {
      "description": "Em Italiano",
      "developer": {
        "url": "http://pontonetbrasil.wordpress.com"
      }
    }
  },
  "default_locale": "en"
}

No arquivo index.html é montado um box com sombra, uma das novidades do CSS3. Coloquei também uma chamada para obter a Latitude e Longitude, algo bem útil para um smartphone, né não?

Saca só:

<!DOCTYPE html>
<html lang="pt-BR">
	<head>
		<title>Firefox OS App</title>
		
		<style>
			.caixa {
				position:relative;
				float:left;
				padding:1em;
				margin:2em 10px 4em;
				background:#fff;
				box-shadow:0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;
			}	

			.caixa:before,
			.caixa:after {
				content:"";
				position:absolute;
				z-index:-2;
			}
			
        .perspectiva:before {
            left:80px;
            bottom:5px;
            width:50%;
            height:35%;
            max-width:200px;
            max-height:50px;
			box-shadow:-80px 0 8px rgba(0, 0, 0, 0.4);
			transform:skew(50deg);
            transform-origin:0 100%;
        }

        .perspectiva:after {
            display:none;
        }
		</style>
		
		<script type="text/javascript" src="jquery.js"></script>
	<head>
	
	<body>
	
		<div class="caixa perspectiva">
            <p id="frase">Firefox OS</p>
			<p><input id="texto" type="text" /><button id="btnTexto">Texto</button></p>
			<p><button id="btnLocalizacao">Latitude/Longitude</button></p>
        </div>
		
		<script>
			$(document).ready(function(){
				$("#btnTexto").click(function(){
					var texto = $("#texto").val();
					$("#frase").text(texto);
				});
				
				$("#btnLocalizacao").click(function(){
					navigator.geolocation.getCurrentPosition(function(posicao){
						$("#frase").text("Latitude: " + posicao.coords.latitude + " - Longitude: " + posicao.coords.longitude);
					});
				});
			});
		</script>

		
	</body>
	
</html>

Bem simples: Nessa página tem uma caixa de texto e um botão “ok”. Ao clicar em “ok”, mostra o texto escrito no input.
Já o botão Latitude/Longitude obtém a Latitude e Longitude.

Eu acredito piamente que não é necessário explicar o que é aquele arquivo chamado jquery.js, certo?

Feito “tudo isso”, agora vamos testar.
Clique em Add Directory e selecione o arquivo manifest.webapp como é mostrado na imagem.
selecionando_app

E o resultado final:

Baixe Aqui!!

Bom, espero que você tenha ficado tão empolgado quanto eu e se quiser saber mais sobre como desenvolver Aplicativos para o Firefox OS acesse aqui https://marketplace.firefox.com/developers/
E se você quiser comprar um aparelho, pelo menos pra testar, saca só esse site: http://www.geeksphone.com/

Por hoje é só pe-pe-pe-pessoal..

Angelo Belchior

Trilha Sonora do Post:

Kamelot : Center Of The Universe

Korzus : 2012

Criando um sistema baseado em Plugins

Olá Pessoal!!

Hoje vou falar um pouco sobre como criar um sistema baseado em plugins. ( A famosa composição!!! hehehe)

Imagine um sistema onde eu possa adicionar, remover ou substituir alguma funcionalidade de forma simples e rápida e sem ter que recompilar, apenas colocando ou tirando arquivos de uma determinada pasta.

Certa vez, precisei criar um sistema para análise de concorrência.

Era simples: esse sistema visitava vários sites a cada 20 ou 30 minutos e obtinha informações.

Porém, cada site tinha seu layout exclusivo, e eu precisei fazer um bendito crawling das páginas, uma por uma – usei o Html Agility Pack - logo mais tem post sobre isso ;-).

A grande questão era que esse sistema não podia sair do ar e os sites quase sempre mudavam o layout, obrigando a ajustar a rotina que obtinha as informações.

Eu tinha, então, que fazer algo que eu pudesse mudar de maneira simples e rápida, sem quebrar nada!!

Sendo assim, lembrei de um tal de MEF - Managed Extensibility Framework, e resolvi dar uma estudadinha nele  pra saber como ele poderia me ajudar.

Abaixo eu mostro um exemplo básico, mas que ilustra bem como usar o MEF.

Mas se você quiser ver como será esse exemplo dê uma olhadinha aqui

O projeto é bem simples. Um sistema que abre uma janela, e lista todos os plugins de uma determinada pasta num listbox. Cada item da lista representa um plugin, e, ao clicar em uma linha, um formuário é mostrado. Cada plugin tem seu próprio formulário!!

Com o desenrolar do post tudo ficará mais claro!!

Começo mostrando a estrutura do projeto. Não vou mostrar passo-a-passo como criar porque seria cansativo.

estrutura-projeto

Projeto Extensibilidade: Esse é um projeto do tipo Class Library contendo uma interface: Segue o Código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Extensibilidade
{
    public interface IPlugin
    {
        string Titulo { get; }
        void Mostrar();
    }
}

Todo e qualquer plugin criado tem que referenciar esse projeto.

O output path desse projeto foi alterado. Isso significa que o assembly será gerado na pasta informada.

Nesse caso compilaremos em C:\Extensibilidade

Extensibilidade

Projeto Plugin1: Esse é um projeto do tipo Class Library contendo um Windows Form básico chamdo Form1 com apenas um label escrito “Plugin 1″ e  uma class chamada Plugin1. Adicionei referências para o Projeto Extensibilidade e para o assembly System.ComponentModel.Composition:

referencia

Código da classe Plugin1:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plugin1
{
    [Export(typeof(Extensibilidade.IPlugin))]
    public class Plugin1 : Extensibilidade.IPlugin
    {
        public string Titulo
        {
            get { return "Plugin 1"; }
        }

        public void Mostrar()
        {
            var form = new Form1();
            form.Show();
        }
    }
}

É importante destacar a linha [Export(typeof(Extensibilidade.IPlugin))]

Ela indica que essa classe será exposta para ser carregada pelo MEF.

Projeto Plugin2: Idêntico ao Projeto Plugin1.

Porém, obviamente, teremos a classe Plugin2  e o form Form2  com um label escrito “Plugin 2″ :-)

O Projeto Plugins é um projeto do tipo Windows Forms. Nesse projeto são importadas as referências para o Projeto Extensibilidade e para o assembly System.ComponentModel.Composition.

Tanto no Projeto Plugin1 e Plugin2 o output path foi alterado:

output path - plugins

Nele temos uma classe chamada Composicao:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace Plugins
{
    public class Composicao
    {
        [ImportMany(AllowRecomposition = true)]
        public IEnumerable Plugins { get; set; }
    }
}

Perceba a linha [ImportMany(AllowRecomposition = true)]. 

Nada mais é do que uma decoração à propriedade Plugins.

Essa propriedade é uma lista de Extensibilidade.IPlugin.

Resumindo: Quando o MEF carregar os plugins de uma determinada pasta, armazenará nessa propriedade a referência para os objetos (classes do assembly que são decoradas com [Export(typeof(Extensibilidade.IPlugin))])

Agora é a vez do Form1.

Nesse form contém apenas um listbox. 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;
using System.IO;
using System.Threading;

namespace Plugins
{
    public partial class Form1 : Form
    {
        private Composicao _composicao = new Composicao();
        private string _pastaPlugins = @"C:\Extensibilidade\Plugins";
        private DirectoryCatalog _diretorio;
        private FileSystemWatcher fileSystemWatcher;

        public Form1()
        {
            InitializeComponent();

            this.Load += new System.EventHandler(this.Form1_Load);
            this.lstPlugins.MouseDoubleClick += lstPlugins_MouseDoubleClick;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.ObservaPasta();
            this.CarregaPlugins();
        }

        private void lstPlugins_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            var pluginClicado = this._composicao.Plugins.FirstOrDefault(p => p.Titulo.Equals(lstPlugins.SelectedItem.ToString()));
            if (pluginClicado != null)
                pluginClicado.Mostrar();
        }

        private void ObservaPasta()
        {
            fileSystemWatcher = new FileSystemWatcher(this._pastaPlugins, "*.dll");
            fileSystemWatcher.Changed += (sender, e) => this.AtualizaPlugins();
            fileSystemWatcher.Created += (sender, e) => this.AtualizaPlugins();
            fileSystemWatcher.Deleted += (sender, e) => this.AtualizaPlugins();
            fileSystemWatcher.Renamed += (sender, e) => this.AtualizaPlugins();
            fileSystemWatcher.IncludeSubdirectories = false;
            fileSystemWatcher.EnableRaisingEvents = true;
        }

        private void CarregaPlugins()
        {
            this._diretorio = new DirectoryCatalog(this._pastaPlugins);
            var catalogo = new AggregateCatalog();
            catalogo.Catalogs.Add(this._diretorio);

            var container = new CompositionContainer(catalogo);
            container.ComposeParts(this._composicao);
            this.ListaPluginsCarregados();
        }

        private void AtualizaPlugins()
        {
            this._diretorio.Refresh();
            this.ListaPluginsCarregados();
        }

        public void ListaPluginsCarregados()
        {
            if (lstPlugins.InvokeRequired)
            {
                lstPlugins.Invoke(
                  new ThreadStart(delegate
                  {
                      lstPlugins.Items.Clear();
                  }));
            }
            else
            {
                lstPlugins.Items.Clear();
            }

            foreach (var plugin in this._composicao.Plugins)
            {
                if (lstPlugins.InvokeRequired)
                {
                    lstPlugins.Invoke(
                      new ThreadStart(delegate
                    {
                        lstPlugins.Items.Add(plugin.Titulo);
                    }));
                }
                else
                {
                    lstPlugins.Items.Add(plugin.Titulo);
                }
            }
        }
    }
}

No construtor assinamos o evento load do form e o MouseDoubleClick do Listbox.

No load fo formulário a pasta que contém os plugins começa a ser observada.

Método ObservaPasta:Aqui é usado o FileSystemWatcher  uma classe para monitorar alterações de uma determinada pasta. Para saber mais sobre ele acesse a documentação.A

A pasta a ser monitorada é a C:\Extensibilidade\Plugins. É aqui onde os plugins serão compilados.

Toda vez que ocorrer uma alteração de arquivos nessa pasta o método AtualizaPlugins será acionado. Nesse método iremos listar todos os plugins no listbox  e atualizar o catálogo de plugins.

Vamos ao CarregaPlugins:

this._diretorio = new DirectoryCatalog(this._pastaPlugins);

Antes de mais nada, a ideia dos catálogos é permitir juntar várias formas de obter esse “plugins”, seja a partir de um Diretório específico, um Assembly ou tipos específicos.

DirectoryCatalog: Essa classe obtém todos os assemblies do diretório informado para identificar qual classes devem ser exportadas ou importadas… lembram da anotação ImportMany e Export?

Além do DirectoryCatalog temos também o AssemblyCatalog:

this._assemblyCatalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());

Só que nesse caso ao invés de procura em uma pasta, ele procurar pelas classes de import/export em um determinado Assembly. 

Temos também o AggregateCatalog, que como o próprio nome diz, tem a finalidade de agregar vários tipos de catalogos.

Fora esses, temos ainda o TypeCatalog, :

this._typeCatalog = new TypeCatalog(typeof(type1), typeof(type2), ...);

Nesse caso ele procura por exports/imports apenas nos tipos informados!!

Mais informações acesse http://mef.codeplex.com/wikipage?title=Using%20Catalogs!!!

CompositionContainer:

var container = new CompositionContainer(catalogo);
container.ComposeParts(this._composicao);

É aqui onda as instâncias dos plugins são criadas e armazenadas em this._composicao.
Uma coisa interessante é que essa propriedade pode importar várias instâncias (ela está decorada com ImportMany), se você quiser, pode importar só uma, basta usar o Import,e, obviamente, ao invés de usar o

[ImportMany(AllowRecomposition = true)]
public IEnumerable Plugins { get; set; }

use

[Import(AllowRecomposition = true)]
public Extensibilidade.IPlugin Plugin { get; set; }

Blz??

Atenção com o método ListaPluginsCarregados.
Nele é feito um tratamento para concorrência de Threads, já que esse método é chamado quando uma pasta é manipulada, e o FileSystemWatcher dispara seus eventos em Threads separadas.
Leia mais sobre isso aqui: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx

Bom pessoal, por hoje é isso!! Essa parada de composição é muito legal, vale a pena dar uma estudada. Se você nunca precisou, com certeza vai precisar!!

E…pra baixar essa maluquice toda: http://sdrv.ms/112YoI6

Até a próxima!!!

Angelo Belchior

Trilha Sonora do Post:

Rage : Innocent (Live Wacken 07 with Lingua Mortis)

Falconer : Tower Of The Queen

Asp.Net Web Api + OData

Asp.Net Web Api é um framework que permite disponibilizar de forma rápida e simples Serviços HTTP.

Em resumo, se você quer criar API’s para serem consumidas por smartphones, tablets, smarttvs, etc., ESSE É O CARA!!

Saiba mais aqui: http://www.asp.net/web-api

O OData é um Protocolo Web para Consulta e Atualização de Dados.

OData: http://www.odata.org

Nesse caso, trataremos apenas de consulta de dados…mas, imagine um serviço que retorna uma lista de Carros. Usando o OData será possível, ao consumir esse serviço, informar quantos itens eu quero de retorno, ordenado por determinada propriedade e, o melhor de tudo, eu consigo informar um filtro.. .como se fosse um Where de um Select

Ao ler a documentação, vai perceber que existem várias constantes para consulta e atualização de dados, porém, é bom frisar que, apenas as constantes $top, $skip, $filter e $orderby estão disponíveis para uso em conjunto do Asp.Net Web Api… mas convenhamos, já ajudam bastante!!

Vamos ao Código!!!

Primeiro, criaremos um Projeto Asp.Net Web Api.

New Project, Asp.Net MVC 4 Application.

Criando um Projeto Asp.Net MVC

Criando um Projeto Asp.Net MVC

Em seguida selecione Web Api.

Selecionando o tipo de Projeto Asp.Net Web Api

Selecionando o tipo de Projeto Asp.Net Web Api

Na pasta Models, crie uma classe chamada Banda.

Essa classe é uma entidade bem simples:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApiOData.Models
{
    public class Banda
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Pais { get; set; }
    }
}

Agora, crie uma classe chamada Repositorio na pasta Models.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApiOData.Models
{
    public class Repositorio
    {
        private List<Banda> Bandas { get; set; }

        public IQueryable<Banda> Listar()
        {
            return this.Bandas.AsQueryable();
        }

        public Repositorio()
        {
            this.Bandas = new List<Banda>();
            this.Bandas.Add(new Banda { Id = 1, Nome = "Iron Maiden", Pais = "Inglaterra" });
            this.Bandas.Add(new Banda { Id = 2, Nome = "AC/DC", Pais = "Australia" });
            this.Bandas.Add(new Banda { Id = 3, Nome = "Metallica", Pais = "EUA" });
            this.Bandas.Add(new Banda { Id = 4, Nome = "Angra", Pais = "Brasil" });
            this.Bandas.Add(new Banda { Id = 5, Nome = "Sabaton", Pais = "Suécia" });
        }
    }
}

Basicamente, essa classe simula uma consulta no banco de dados.
A ideia aqui, não é aplicar nenhum padrão-estado-da-arte da programação, longe disso…

No construtor eu carrego a lista com algumas bandas que eu gosto.

Se alguma banda ai não te agrade, apenas troque. Sem trollagens, blz??

O Método Listar, retorna todas as bandas armazenadas na minha lista de Bandas.

Porém, o retorno do método não é um IEnumerable<T> ou um List<T> como é de costume, e sim o IQueryable<T>.

O IQueryable proporciona toda infraestrutura para o OData fazer todo o trabalho de consulta automágicamente.

Veremos como funciona a seguir…

Em seguida, clique com o botão direito do mouse na pasta Controllers -> Add -> Controller.
Dê o nome de Bandas e clique em Add.

Um controller chamado BandasController foi criado.

Saca só o código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApiOData.Controllers
{
    public class BandasController : ApiController
    {
        [HttpGet]
        public IQueryable<Models.Banda> Listar()
        {
            Models.Repositorio repositorio = new Models.Repositorio();
            var bandas = repositorio.Listar();

            return bandas;
        }
    }
}

Aqui, simplesmente retornamos a lista de Bandas do Repositório… nada além disso.

Dê F5!!

Vamos testar a nossa listagem:

Acesse http://localhost:PORTA/api/Bandas/Listar

listar

Uma lista de bandas é apresentada…

Agora, esfregue as mãos e tente esses links abaixo.

http://localhost:PORTA/api/Bandas/Listar/?$top=2

$top=2

http://localhost:PORTA/api/Bandas/Listar/?$orderby=Nome asc

$orderby=Nome

http://localhost:PORTA/api/Bandas/Listar/?$skip=2&$top=2&$orderby=Nome

$skip=2&$top=2&$orderby=Nome

http://localhost:PORTA/api/Bandas/Listar/?$filter=Id%20ge%203

$filter=Id ge 3

http://localhost:PORTA/api/Bandas/Listar/?$filter=endswith(Nome,’Maiden’)

$filter=endswith(Nome,'Maiden')

Sacou a idéia?

$top=2 indica que apenas os dois primeiros itens serão retornados

$orderby=Nome indica que a listagem será ordenada pelo Nome.

Se eu quisesse ordenar por mais de um campo, é simples: $orderby=Nome,Id,Pais.

Além disso, podemos usar o asc e o desc: $orderby=Nome asc $orderby=Nome desc

Tá rolando até uma paginação sapeca com o $skip=2&$top=2!!!

Eu imagino que você já tenha entendido o que o $filter faz. Sendo assim, vamos a algo mais útil:

Abaixo uma listagem com todos os parâmentros possíveis:

Copiado descaradamente daqui: http://www.odata.org/documentation/uri-conventions#FilterSystemQueryOption

Operator Description Example
Logical Operators
Eq Equal /Suppliers?$filter=Address/City eq ‘Redmond’
Ne Not equal /Suppliers?$filter=Address/City ne ‘London’
Gt Greater than /Products?$filter=Price gt 20
Ge Greater than or equal /Products?$filter=Price ge 10
Lt Less than /Products?$filter=Price lt 20
Le Less than or equal /Products?$filter=Price le 100
And Logical and /Products?$filter=Price le 200 and Price gt 3.5
Or Logical or /Products?$filter=Price le 3.5 or Price gt 200
Not Logical negation /Products?$filter=not endswith(Description,’milk’)
Arithmetic Operators
Add Addition /Products?$filter=Price add 5 gt 10
Sub Subtraction /Products?$filter=Price sub 5 gt 10
Mul Multiplication /Products?$filter=Price mul 2 gt 2000
Div Division /Products?$filter=Price div 2 gt 4
Mod Modulo /Products?$filter=Price mod 2 eq 0
Grouping Operators
( ) Precedence grouping /Products?$filter=(Price sub 5) gt 10

Dú caralho, fala aê?

Agora, recomendo a você uma boa lida na documentação do ODatahttp://www.odata.org/documentation/uri-conventions

Blz??

Pra baixar: http://sdrv.ms/VDaDpG

Por hoje é só!

Angelo Belchior

Trilha Sonora do Post:

Sabaton : Coat of Arms