Remote.Linq is a small and easy to use - yet very powerful - library to translate LINQ expression trees to strongly typed, serializable expression trees and vice versa. It provides functionality to send arbitrary LINQ queries to a remote service to be applied and executed against any enumerable or queryable data collection.
Building a LINQ interface for custom services is made a breeze by using Remote.Linq.
In contrast to re-linq, this project enables serialization and deserialization of expression trees and applying LINQ expressions to other LINQ providers e.g. linq-to-object, linq-to-entity, etc.
Remote.Linq makes it super easy to implement a service allowing LINQ queries defined on a client to be executed on a remote server.
Write operations (insert/update/delete) have to be implemented by other means if needed. InfoCarrier.Core or EfCore.Client might be interesting for such scenarios.
Check-out Remote.Linq.Samples.sln and samples folder for a number of sample use cases.
Implement a repository class to set-up server connection and expose the queryable data sets (IQueryable<>
)
public class ClientDataRepository { private readonly Func<Expression, DynamicObject> _dataProvider; public RemoteRepository(string uri) { _dataProvider = expression => { // setup service connectivity using IQueryService service = CreateServerConnection(uri); // send expression to service and get back results DynamicObject result = service.ExecuteQuery(expression); return result; }; } public IRemoteQueryable<Blog> Blogs => RemoteQueryable.Factory.CreateQueryable<Blog>(_dataProvider); public IRemoteQueryable<Post> Posts => RemoteQueryable.Factory.CreateQueryable<Post>(_dataProvider); public IRemoteQueryable<User> Users => RemoteQueryable.Factory.CreateQueryable<User>(_dataProvider); // where IRemoteQueryable<out T> is IQueryable<out T> }
Use your repository to compose LINQ query and let the data be retrieved from the backend service
var repository = new ClientDataRepository("https://myserver/queryservice"); var myBlogPosts = ( from blog in repository.Blogs from post in blog.Posts join owner in repository.Users on blog.OwnerId equals owner.Id where owner.login == "hi-its-me" select new { post.Title, post.Date, Preview = post.Text.Substring(0, 50) }).ToList();
Implement the backend service to handle the client's query expression by applying it to a data source e.g. an ORM
public interface IQueryService : IDisposable { DynamicObject ExecuteQuery(Expression queryExpression); } public class QueryService : IQueryService { // any linq provider e.g. entity framework, nhibernate, ... private IDataProvider _datastore = new ObjectRelationalMapper(); // you need to be able to retrieve an IQueryable by type private Func<Type, IQueryable> _queryableProvider = type => _datastore.GetQueryableByType(type); public DynamicObject ExecuteQuery(Expression queryExpression) { // `Execute` is an extension method provided by Remote.Linq // it applies an expression to a data source and returns the result return queryExpression.Execute(queryableProvider: _queryableProvider); } public void Dispose() => _datastore.Dispose(); }
IAsyncRemoteQueryable<TEntity> asyncQuery = RemoteQueryable.Factory.CreateAsyncQueryable<TEntity>(...); TEntity[] result = await asyncQuery.ToArrayAsync().ConfigureAwait(false); // where interface IAsyncRemoteQueryable<out T> is IRemoteQueryable<out T> is IQueryable<out T>
IAsyncRemoteStreamQueryable<TEntity> asyncStreamQuery = RemoteQueryable.Factory.CreateAsyncStreamQueryable<TEntity>(...); await foreach (TEntity item in asyncStreamQuery.ConfigureAwait(false)) { } // where interface IAsyncRemoteStreamQueryable<out T> is IQueryable<out T>
See MS tutorial on async streams for more info.
Remote.Linq.Async.QueryableProvides interoperability with Interactive Extensions (Ix.NET / System.Linq.Async.Queryable).
System.Linq.IAsyncQueryable<TEntity> asyncQuery = RemoteQueryable.Factory.CreateAsyncQueryable<TEntity>(...); await foreach (TEntity item in asyncQuery.ConfigureAwait(false)) { }Remote.Linq.EntityFramework / Remote.Linq.EntityFrameworkCore
Remote linq extensions for Entity Framework and Entity Framework Core.
These packages are used on server side to apply queries to EFs DbContext
.
The only reason to include one of these packages in a client side project is to enable utilization of query features specific to EF6 and EF Core:
Apply eager-loading (Include
-expressions)
Make use of DB functions
e.g. queryable.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Name, "%fruit%"))
Query blogs including (i.e. eager loading) posts and owners
using var repository = new RemoteRepository(); var blogs = repository.Blogs .Include(x => x.Posts) .ThenInclude(x => x.Owner) .ToList();
Execute query on database via EF Core
public DynamicObject ExecuteQuery(Expression queryExpression) { using var dbContext = new DbContext(); return queryExpression.ExecuteWithEntityFrameworkCore(dbContext); }Remote.Linq.Newtonsoft.Json
Provides Json.NET serialization settings for Remote.Linq types.
// Serialization TValue value = ...; JsonSerializerSettings serializerSettings = new JsonSerializerSettings().ConfigureRemoteLinq(); string json = JsonConvert.SerializeObject(value, serializerSettings); TValue copy = JsonConvert.DeserializeObject<TValue>(json, serializerSettings);
Provides System.Text.Json serialization settings for Remote.Linq types.
// Serialization TValue value = ...; JsonSerializerOptions serializerOptions = new JsonSerializerOptions().ConfigureRemoteLinq(); string json = JsonSerializer.Serialize(value, serializerOptions); TValue copy = JsonSerializer.Deserialize<TValue>(json, serializerOptions);
// Microsoft.AspNetCore.Hosting [WebHostBuilder] new WebHostBuilder() .ConfigureServices(services => services .AddMvcCore() .AddJsonOptions(options => options.JsonSerializerOptions.ConfigureRemoteLinq()));
// Microsoft.AspNetCore.Hosting [WebApplicationBuilder] var builder = WebApplication.CreateBuilder(); builder.Services .AddMvcCore() .AddJsonOptions(options => options.JsonSerializerOptions.ConfigureRemoteLinq());
// System.Net.Http.HttpClient TValue value = ...; Uri uri = ...; var serializerOptions = new JsonSerializerOptions().ConfigureRemoteLinq(); using var httpClient = new HttpClient(); await httpClient.PostAsJsonAsync(uri, value, serializerOptions);
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4