OdataToEntity.Linq2Db 1.4.1

Linq2Db adapter for OdataEntity

Classes bridge from OdataToEntity to Linq2Db. Abstraction layer access to DataContext Entity Linq2Db. Targets .NET 4.6

Install-Package OdataToEntity.Linq2Db -Version 1.4.1
dotnet add package OdataToEntity.Linq2Db --version 1.4.1
paket add OdataToEntity.Linq2Db --version 1.4.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

OdataToEntity

Travis

OData .net core

This library provides a simple approach to creating OData service from ORM data context.
This translates the OData query into an expression tree and passes it to the ORM framework.
Supported ORM: Entity Framework 6, Entity Framework Core, Linq2Db

public sealed class OrderContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Order> Orders { get; set; }

    public IEnumerable<Order> GetOrders(int? id, String name, OrderStatus? status) => throw new NotImplementedException();
}

Build OData EdmModel

Buid from entity classes marked data annotation attribute, the general case for all providers

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Build OData Edm Model
EdmModel edmModel = dataAdapter.BuildEdmModel();

Build from the Entity Framework Core where the data context uses the "Fluent API" (without using attributes)

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Build OData Edm Model
EdmModel edmModel = dataAdapter.BuildEdmModelFromEfCoreModel();

Build from the Entity Framework 6 where the data context uses the "Fluent API" (without using attributes)

//Create adapter data access, where OrderEf6Context your DbContext
var dataAdapter = new OeEf6DataAdapter<OrderEf6Context>();
//Build OData Edm Model
EdmModel edmModel = dataAdapter.BuildEdmModelFromEf6Model();

Sample OData query

For use odata.nextLink, the next link of a collection with partial results set property PageSize class OeParser
For use to-many navigation property odata.nextLink, the next link of a collection with results set property NavigationNextLink class OeParser

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel())
{
  NavigationNextLink = true,
  PageSize = 2
};
//Query
var uri = new Uri("http://dummy/Orders?$select=Name");
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteGetAsync(uri, OeRequestHeaders.JsonDefault, response, CancellationToken.None);

Sample OData batch request

string batch = @"
--batch_6263d2a1-1ddc-4b02-a1c1-7031cfa93691
Content-Type: multipart/mixed; boundary=changeset_e9a0e344-4133-4677-9be8-1d0006e40bb6

--changeset_e9a0e344-4133-4677-9be8-1d0006e40bb6
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

POST http://dummy/Customers HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft ADO.NET Data Services

{""@odata.type"":""#OdataToEntity.Test.Model.Customer"",""Address"":""Moscow"",""Id"":1,""Name"":""Ivan"",""Sex@odata.type"":""#OdataToEntity.Test.Model.Sex"",""Sex"":""Male""}

--changeset_e9a0e344-4133-4677-9be8-1d0006e40bb6--
--batch_6263d2a1-1ddc-4b02-a1c1-7031cfa93691--
";

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel());
//Serialized entities in JSON UTF8 format
var request = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(batch));
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteBatchAsync(request, response, CancellationToken.None);

Sample OData stored procedure

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel());
//The result of the stored procedure
var response = new MemoryStream();
//Execute sored procedure
await parser.ExecuteGetAsync(new Uri("http://dummy/GetOrders(name='Order 1',id=1,status=null)"), OeRequestHeaders.JsonDefault, response, CancellationToken.None);

Server-Driven Paging

To use responses that include only a partial set of the items identified by the request indicate maximum page size through the invoke method OeRequestHeaders.SetMaxPageSize(int maxPageSize). The service serializes the returned continuation token into the $skiptoken query option and returns it as part of the next link to the client. If request returns result set sorted by nullable database column, should set OeDataAdapter.IsDatabaseNullHighestValue (SQLite, MySql, Sql Server set false, for PostgreSql, Oracle set true), or mark property RequiredAttribute.

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>(Model.OrderContext.CreateOptions())
{
  IsDatabaseNullHighestValue = true //PostgreSql
};
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel());
//Query
var uri = new Uri("http://dummy/Orders?$select=Name&$orderby=Date");
//Set max page size
OeRequestHeaders requestHeaders = OeRequestHeaders.JsonDefault.SetMaxPageSize(10);
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteGetAsync(uri, requestHeaders, response, CancellationToken.None);

To use server side paging in expanded to-many navigation properties, should invoke method OeRequestHeaders.SetNavigationNextLink(true)

//Query
var uri = new Uri("http://dummy/Orders?$expand=Items");
//Set max page size,  to-many navigation properties
OeRequestHeaders requestHeaders = OeRequestHeaders.JsonDefault.SetMaxPageSize(10).SetNavigationNextLink(true);
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteGetAsync(uri, requestHeaders, response, CancellationToken.None);

A function specific to the Entity Framework Core

For the Entity Framework Core provider, there is the ability to cache queries, which on existing tests allows you to increase the performance up to two times. The cache key is a parsed OData query with remote constant values, and the value is a delegate accepting the data context and returning the query result. This allows to exclude the stage of building the data query itself (IQueryable). To use this feature, you need to use the constructor OeEfCoreDataAdapter(DbContextOptions options, Db.OeQueryCache queryCache).

For use pooling (DbContextPool) in Entity Framework Core create instance OeEfCoreDataAdapter use constructor with DbContextOptions parameter.

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>(Model.OrderContext.CreateOptions());

Project structure

Library source/OdataEntity.
Data context adapter Entity Framework Core - source\OdataToEntity.EfCore.
Data context adapter Entity Framework 6.2 - source\OdataToEntity.Ef6.
Data context adapter Linq2Db - source\OdataToEntity.Linq2Db.
Routing, middleware, controller base class - source\OdataToEntity.AspNetCore.
Client Microsoft.OData.Client - test\OdataToEntity.Test.Asp\OdataToEntity.Test.AspClient.
Server Asp .net core - test\OdataToEntity.Test.Asp\OdataToEntity.Test.AspServer.
Server Asp mvc .net core - test\OdataToEntity.Test.Asp\OdataToEntity.Test.AspMvcServer.

Script create Sql Server database - \test\OdataToEntity.Test.EfCore.SqlServer\script.sql.
Script create PostgreSql database - \test\OdataToEntity.Test.EfCore.PostgreSql\script.sql.

OdataToEntity

Travis

OData .net core

This library provides a simple approach to creating OData service from ORM data context.
This translates the OData query into an expression tree and passes it to the ORM framework.
Supported ORM: Entity Framework 6, Entity Framework Core, Linq2Db

public sealed class OrderContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Order> Orders { get; set; }

    public IEnumerable<Order> GetOrders(int? id, String name, OrderStatus? status) => throw new NotImplementedException();
}

Build OData EdmModel

Buid from entity classes marked data annotation attribute, the general case for all providers

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Build OData Edm Model
EdmModel edmModel = dataAdapter.BuildEdmModel();

Build from the Entity Framework Core where the data context uses the "Fluent API" (without using attributes)

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Build OData Edm Model
EdmModel edmModel = dataAdapter.BuildEdmModelFromEfCoreModel();

Build from the Entity Framework 6 where the data context uses the "Fluent API" (without using attributes)

//Create adapter data access, where OrderEf6Context your DbContext
var dataAdapter = new OeEf6DataAdapter<OrderEf6Context>();
//Build OData Edm Model
EdmModel edmModel = dataAdapter.BuildEdmModelFromEf6Model();

Sample OData query

For use odata.nextLink, the next link of a collection with partial results set property PageSize class OeParser
For use to-many navigation property odata.nextLink, the next link of a collection with results set property NavigationNextLink class OeParser

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel())
{
  NavigationNextLink = true,
  PageSize = 2
};
//Query
var uri = new Uri("http://dummy/Orders?$select=Name");
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteGetAsync(uri, OeRequestHeaders.JsonDefault, response, CancellationToken.None);

Sample OData batch request

string batch = @"
--batch_6263d2a1-1ddc-4b02-a1c1-7031cfa93691
Content-Type: multipart/mixed; boundary=changeset_e9a0e344-4133-4677-9be8-1d0006e40bb6

--changeset_e9a0e344-4133-4677-9be8-1d0006e40bb6
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

POST http://dummy/Customers HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft ADO.NET Data Services

{""@odata.type"":""#OdataToEntity.Test.Model.Customer"",""Address"":""Moscow"",""Id"":1,""Name"":""Ivan"",""Sex@odata.type"":""#OdataToEntity.Test.Model.Sex"",""Sex"":""Male""}

--changeset_e9a0e344-4133-4677-9be8-1d0006e40bb6--
--batch_6263d2a1-1ddc-4b02-a1c1-7031cfa93691--
";

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel());
//Serialized entities in JSON UTF8 format
var request = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(batch));
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteBatchAsync(request, response, CancellationToken.None);

Sample OData stored procedure

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>();
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel());
//The result of the stored procedure
var response = new MemoryStream();
//Execute sored procedure
await parser.ExecuteGetAsync(new Uri("http://dummy/GetOrders(name='Order 1',id=1,status=null)"), OeRequestHeaders.JsonDefault, response, CancellationToken.None);

Server-Driven Paging

To use responses that include only a partial set of the items identified by the request indicate maximum page size through the invoke method OeRequestHeaders.SetMaxPageSize(int maxPageSize). The service serializes the returned continuation token into the $skiptoken query option and returns it as part of the next link to the client. If request returns result set sorted by nullable database column, should set OeDataAdapter.IsDatabaseNullHighestValue (SQLite, MySql, Sql Server set false, for PostgreSql, Oracle set true), or mark property RequiredAttribute.

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>(Model.OrderContext.CreateOptions())
{
  IsDatabaseNullHighestValue = true //PostgreSql
};
//Create query parser
var parser = new OeParser(new Uri("http://dummy"), dataAdapter, dataAdapter.BuildEdmModel());
//Query
var uri = new Uri("http://dummy/Orders?$select=Name&$orderby=Date");
//Set max page size
OeRequestHeaders requestHeaders = OeRequestHeaders.JsonDefault.SetMaxPageSize(10);
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteGetAsync(uri, requestHeaders, response, CancellationToken.None);

To use server side paging in expanded to-many navigation properties, should invoke method OeRequestHeaders.SetNavigationNextLink(true)

//Query
var uri = new Uri("http://dummy/Orders?$expand=Items");
//Set max page size,  to-many navigation properties
OeRequestHeaders requestHeaders = OeRequestHeaders.JsonDefault.SetMaxPageSize(10).SetNavigationNextLink(true);
//The result of the query
var response = new MemoryStream();
//Execute query
await parser.ExecuteGetAsync(uri, requestHeaders, response, CancellationToken.None);

A function specific to the Entity Framework Core

For the Entity Framework Core provider, there is the ability to cache queries, which on existing tests allows you to increase the performance up to two times. The cache key is a parsed OData query with remote constant values, and the value is a delegate accepting the data context and returning the query result. This allows to exclude the stage of building the data query itself (IQueryable). To use this feature, you need to use the constructor OeEfCoreDataAdapter(DbContextOptions options, Db.OeQueryCache queryCache).

For use pooling (DbContextPool) in Entity Framework Core create instance OeEfCoreDataAdapter use constructor with DbContextOptions parameter.

//Create adapter data access, where OrderContext your DbContext
var dataAdapter = new OeEfCoreDataAdapter<Model.OrderContext>(Model.OrderContext.CreateOptions());

Project structure

Library source/OdataEntity.
Data context adapter Entity Framework Core - source\OdataToEntity.EfCore.
Data context adapter Entity Framework 6.2 - source\OdataToEntity.Ef6.
Data context adapter Linq2Db - source\OdataToEntity.Linq2Db.
Routing, middleware, controller base class - source\OdataToEntity.AspNetCore.
Client Microsoft.OData.Client - test\OdataToEntity.Test.Asp\OdataToEntity.Test.AspClient.
Server Asp .net core - test\OdataToEntity.Test.Asp\OdataToEntity.Test.AspServer.
Server Asp mvc .net core - test\OdataToEntity.Test.Asp\OdataToEntity.Test.AspMvcServer.

Script create Sql Server database - \test\OdataToEntity.Test.EfCore.SqlServer\script.sql.
Script create PostgreSql database - \test\OdataToEntity.Test.EfCore.PostgreSql\script.sql.

Version History

Version Downloads Last updated
1.4.1 (current version) 57 4/1/2018
1.4.0 61 3/4/2018
1.3.0 88 11/5/2017
1.2.0 95 8/28/2017
1.1.1 107 8/1/2017