
Tradicionalmente, as consultas OData nos dados são expressas como seqüências simples, sem verificação de tipo durante a compilação ou sem o suporte ao IntelliSense. Além disso, o desenvolvedor precisa aprender a sintaxe do idioma da consulta. Este artigo descreve a biblioteca TsToOdata, que transforma consultas em uma construção de linguagem conveniente e é aplicada de maneira semelhante a classes e métodos. Você cria consultas digitadas usando palavras-chave TypeScript e operadores familiares.
TsToOdata é uma biblioteca para TypeScript. É semelhante ao LINQ para C #, mas, diferentemente do último, destina-se apenas a consultas OData. Para o desenvolvedor que cria as consultas, a parte mais óbvia do TsToOdata é a expressão de consulta. As expressões de consulta usam sintaxe declarativa; portanto, o desenvolvedor grava o que precisa ser feito, sem especificar como fazê-lo. Usando a sintaxe da consulta, você pode filtrar, organizar e agrupar dados de uma fonte de dados, usando uma quantidade mínima de código de programa.
Instale o TsToOdata
npm install ts2odata
Criando um modelo de dados
Antes de tudo, precisamos obter o mapeamento do esquema OData para as classes TypeScript.
O primeiro passo é obter primeiro o esquema do EDMX Json. Você pode usar a biblioteca OdataToEntity para isso .
IEdmModel edmModel;
using (var reader = XmlReader.Create("edmx_schema.xml"))
edmModel = CsdlReader.Parse(reader);
var generator = new OeJsonSchemaGenerator(edmModel);
using (var utf8Json = new MemoryStream())
{
generator.Generate(utf8Json);
utf8Json.Position = 0;
File.WriteAllBytes("json_schema.json", utf8Json.ToArray());
}
Json TypeScript. quicktype.
.
import { EntitySet, OdataContext } from 'ts2odata';
import * as oe from './order';
export class OrderContext extends OdataContext<OrderContext> {
Categories = EntitySet.default<oe.Category>();
Customers = EntitySet.default<oe.Customer>();
OrderItems = EntitySet.default<oe.OrderItem>();
OrderItemsView = EntitySet.default<oe.OrderItemsView>();
Orders = EntitySet.default<oe.Order>();
}
let context: OrderContext = OdataContext.create(OrderContext, 'http://localhost:5000/api');
context.Orders;
context.Orders.select(o => { return { p: o.Name } });
context.Orders.orderby(i => i.Id);
context.Orders.orderbyDescending(i => i.Id);
context.Orders.filter(o => o.Date.getFullYear() == 2016);
context.Orders.expand(o => o.Items);
context.Customers.expand(c => c.Orders).thenExpand(o => o.Items);
context.Orders.orderby(i => i.Id).skip(2);
context.Orders.orderby(i => i.Id).top(3);
context.OrderItems.groupby(i => { return { Product: i.Product } });
context.OrderItems.groupby(i => { return { OrderId: i.OrderId, Status: i.Order.Status } })
.select(g => {
return {
orderId: g.key.OrderId,
avg: g.average(i => i.Price),
dcnt: g.countdistinct(i => i.Product),
max: g.max(i => i.Price),
max_status: g.max(_ => g.key.Status),
min: g.min(i => i.Price),
sum: g.sum(i => i.Price),
cnt: g.count()
}});
context.Customers.key({ Country: 'RU', Id: 1 });
context.OrderItems.key(1, i => i.Order.Customer);
context.OrderItems
.select(i => {
return {
product: i.Product,
Total: i.Count * i.Price,
SumId: i.Id + i.OrderId
}
});
context.Orders.filter(o => o.Items.every(i => i.Price >= 2.1));
context.Orders.filter(o => o.Items.some(i => i.Count > 2));
IN
let items = [1.1, 1.2, 1.3];
context.OrderItems.filter(i => items.includes(i.Price), { items: items });
context.Orders.count();
asEntitySet
context.Orders(o => o.AltCustomer).thenSelect(o => {{
p1: o.Address,
: o.Country,
: o.Id,
: o.Name,
: o.Sex
}}).asEntitySet().orderby(o => o.Id)
GitHub.
, select, expand, groupby — — , , then: thenFilter, thenExpand, thenOrderby, thenOrderbyDescending, thenSkip, thenTop. select thenSelect , , , asEntitySet.
— filter/thenFilter, — select/thenSelect, — groupby , .
let count: number | null = null;
context.OrderItems.filter(i => i.Count == count, { count: count });
let s = {
altCustomerId: 3,
customerId: 4,
dateYear: 2016,
dateMonth: 11,
dateDay: 20,
date: null,
name: 'unknown',
status: "OdataToEntity.Test.Model.OrderStatus'Unknown'",
count1: 0,
count2: null,
price1: 0,
price2: null,
product1: 'unknown',
product2: 'null',
orderId: -1,
id: 1
};
context.Orders.filter(o => o.AltCustomerId == s.altCustomerId &&
o.CustomerId == s.customerId &&
(o.Date.getFullYear() == s.dateYear &&
o.Date.getMonth() > s.dateMonth &&
o.Date.getDay() < s.dateDay ||
o.Date == s.date) &&
o.Name.includes(s.name) &&
o.Status == s.status, s)
.expand(o => o.Items)
.thenFilter(i => (i.Count == s.count1 ||
i.Count == s.count2) &&
(i.Price == s.price1 ||
i.Price == s.price2) &&
(i.Product.includes(s.product1) ||
i.Product.includes(s.product2)) &&
i.OrderId > s.orderId &&
i.Id != s.id, s);
OdataFunctions.stringLength
context.Customers.filter(c => OdataFunctions.stringLength(c.Name) == 5);
OdataFunctions.arrayLength
context.Orders.filter(o => OdataFunctions.arrayLength(o.Items) > 2);
select, filter getQueryUrl toArrayAsync.
getQueryUrl URL . TypeScript:
let url: URL = context.Customers
.expand(c => c.AltOrders).thenExpand(o => o.Items).thenOrderby(i => i.Price)
.expand(c => c.AltOrders).thenExpand(o => o.ShippingAddresses).thenOrderby(s => s.Id)
.expand(c => c.Orders).thenExpand(o => o.Items).thenOrderby(i => i.Price)
.expand(c => c.Orders).thenExpand(o => o.ShippingAddresses).thenOrderby(s => s.Id)
.orderby(c => c.Country).orderby(c => c.Id).getQueryUrl();
OData :
http://localhost:5000/api/Customers?$expand=
AltOrders($expand=Items($orderby=Price),ShippingAddresses($orderby=Id)),
Orders($expand=Items($orderby=Price),ShippingAddresses($orderby=Id))
&$orderby=Country,Id
toArrayAsync Json. TypeScript:
context.Customers
.expand(c => c.Orders).thenSelect(o => { return { Date: o.Date } }).orderby(o => o.Date)
.asEntitySet().select(c => { return { Name: c.Name } }).orderby(c => c.Name).toArrayAsync();
Json:
[{
"Name": "Ivan",
"Orders": [{
"Date": "2016-07-04T19:10:10.8237573+03:00"
}, {
"Date": "2020-02-20T20:20:20.000002+03:00"
}
]
}, {
"Name": "Natasha",
"Orders": [{
"Date": "2016-07-04T19:10:11+03:00"
}
]
}, {
"Name": "Sasha",
"Orders": []
}, {
"Name": "Unknown",
"Orders": [{
"Date": null
}
]
}
]
, , toArrayAsync OdataParser:
import { OdataParser } from 'ts2odata';
import schema from './schema.json';
let odataParser = new OdataParser(schema);
context.Orders.toArrayAsync(odataParser);
(enum)
OData (Namespace), :
let context: OrderContext = OdataContext.create(OrderContext, 'http://localhost:5000/api', 'OdataToEntity.Test.Model');
Em alguns casos, pode ser necessário criar um objeto OdataParser para converter corretamente as enumerações .
import { OdataParser } from 'ts2odata';
import schema from './schema.json';
let odataParser = new OdataParser(schema);
let context: OrderContext = OdataContext.create(OrderContext, 'http://localhost:5000/api', 'OdataToEntity.Test.Model', odataParser);
Fonte
O código fonte está no GitHub .
Na pasta de origem - o código do Nó do pacote, na pasta de teste - testa.
Espero que meu projeto TsToOdata seja útil para você e o salve da rotina de escrever código monótono.