
Tradicionalmente, las consultas OData sobre los datos se expresan como cadenas simples sin verificación de tipo durante la compilación o sin el soporte de IntelliSense, además, el desarrollador tiene que aprender la sintaxis del lenguaje de consulta. Este artículo describe la biblioteca TsToOdata, que convierte las consultas en una construcción de lenguaje conveniente y se aplica de manera similar a las clases y métodos. Puede crear consultas escritas con palabras clave de TypeScript y operadores familiares.
TsToOdata es una biblioteca para TypeScript. Es similar a LINQ para C #, pero, a diferencia de este último, solo está destinado a consultas OData. Para el desarrollador que crea las consultas, la parte más obvia de TsToOdata es la expresión de consulta. Las expresiones de consulta utilizan sintaxis declarativa, por lo que el desarrollador escribe lo que debe hacerse, sin especificar cómo hacerlo. Con la sintaxis de consulta, puede filtrar, organizar y agrupar datos de una fuente de datos, utilizando una cantidad mínima de código de programa.
Instalar TsToOdata
npm install ts2odata
Crear un modelo de datos
En primer lugar, necesitamos obtener la asignación del esquema OData a las clases TypeScript.
El primer paso es obtener primero el esquema de EDMX Json. Puede usar la biblioteca OdataToEntity para esto .
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');
En algunos casos, puede ser necesario crear un objeto OdataParser para traducir correctamente las enumeraciones .
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);
Fuente
El código fuente está en GitHub .
En la carpeta de origen - el código de nodo del paquete, en la carpeta de prueba - pruebas.
Espero que mi proyecto TsToOdata te sea útil y te salve de la rutina de escribir código monótono.