You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.5 KiB
117 lines
3.5 KiB
using Connected.Annotations;
|
|
using Connected.Data.Storage;
|
|
using Connected.Data.Update;
|
|
using Connected.Entities;
|
|
using Connected.Entities.Storage;
|
|
using Connected.Expressions;
|
|
using Connected.Expressions.Evaluation;
|
|
using Connected.Expressions.Query;
|
|
using Connected.Expressions.Translation;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
|
|
namespace Connected.Data.Sql;
|
|
|
|
[Priority(1)]
|
|
internal sealed class SqlStorageProvider : QueryProvider, IStorageExecutor, IStorageMiddleware
|
|
{
|
|
public SqlStorageProvider(IStorageProvider storage)
|
|
{
|
|
Storage = storage;
|
|
}
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
protected override object? OnExecute(Expression expression)
|
|
{
|
|
return CreateExecutionPlan(expression).Compile()(this);
|
|
}
|
|
|
|
private static Expression<Func<IStorageExecutor, object>> CreateExecutionPlan(Expression expression)
|
|
{
|
|
var lambda = expression as LambdaExpression;
|
|
|
|
if (lambda is not null)
|
|
expression = lambda.Body;
|
|
|
|
var context = new ExpressionCompilationContext(new TSqlLanguage());
|
|
var translator = new Translator(context);
|
|
var translation = translator.Translate(expression);
|
|
var parameters = lambda?.Parameters;
|
|
var provider = Resolve(expression, parameters, typeof(IStorage<>));
|
|
|
|
if (provider is null)
|
|
{
|
|
var rootQueryable = Resolve(expression, parameters, typeof(IQueryable));
|
|
|
|
provider = Expression.Property(rootQueryable, typeof(IQueryable).GetTypeInfo().GetDeclaredProperty(nameof(IQueryable.Provider)));
|
|
}
|
|
|
|
return ExecutionBuilder.Build(context, new TSqlLinguist(context, TSqlLanguage.Default, translator), translation, provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find the expression of the specified type, either in the specified expression or parameters.
|
|
/// </summary>
|
|
private static Expression Resolve(Expression expression, IList<ParameterExpression> parameters, Type type)
|
|
{
|
|
if (parameters is not null)
|
|
{
|
|
var found = parameters.FirstOrDefault(p => type.IsAssignableFrom(p.Type));
|
|
|
|
if (found is not null)
|
|
return found;
|
|
}
|
|
|
|
return SubtreeResolver.Resolve(expression, type);
|
|
}
|
|
|
|
public IEnumerable<TResult?> Execute<TResult>(IStorageOperation operation)
|
|
where TResult : IEntity
|
|
{
|
|
/*
|
|
* Currently, only Shared connection is supported. Must find a way to pass
|
|
* behavior into the execution pipeline.
|
|
*/
|
|
var result = Storage.Open<TResult>().Query(new StorageContextArgs(operation));
|
|
|
|
if (result.IsCompleted)
|
|
return result.Result;
|
|
|
|
var r = result.GetAwaiter().GetResult();
|
|
|
|
return r;
|
|
}
|
|
|
|
public bool SupportsEntity(Type entityType)
|
|
{
|
|
return entityType.IsAssignableTo(typeof(Entity));
|
|
}
|
|
|
|
public IStorageOperation CreateOperation<TEntity>(TEntity entity)
|
|
where TEntity : IEntity
|
|
{
|
|
var builder = new AggregatedCommandBuilder<TEntity?>();
|
|
|
|
if (builder.Build(entity) is not StorageOperation operation)
|
|
throw new NullReferenceException(nameof(StorageOperation));
|
|
|
|
return operation;
|
|
}
|
|
|
|
public IStorageReader<TEntity> OpenReader<TEntity>(IStorageOperation operation, IStorageConnection connection)
|
|
{
|
|
return new DatabaseReader<TEntity>(operation, connection);
|
|
}
|
|
|
|
public IStorageWriter OpenWriter(IStorageOperation operation, IStorageConnection connection)
|
|
{
|
|
return new DatabaseWriter(operation, connection);
|
|
}
|
|
|
|
public async Task Initialize()
|
|
{
|
|
await Task.CompletedTask;
|
|
}
|
|
}
|