using System.Linq.Expressions; using System.Reflection; using Connected.Annotations; using Connected.Data; using Connected.Data.Storage; using Connected.Entities; using Connected.Entities.Storage; using Connected.Expressions; using Connected.Expressions.Evaluation; using Connected.Expressions.Query; using Connected.Expressions.Translation; using Connected.Interop; using Connected.ServiceModel.Data; namespace Connected.ServiceModel.Client.Data; [Priority(2)] internal class TableStorageProvider : QueryProvider, IStorageExecutor, IStorageMiddleware { public TableStorageProvider(IStorageProvider storage) { Storage = storage; } private IStorageProvider Storage { get; } protected override object? OnExecute(Expression expression) { return CreateExecutionPlan(expression).Compile()(this); } private static Expression> CreateExecutionPlan(Expression expression) { var lambda = expression as LambdaExpression; if (lambda is not null) expression = lambda.Body; var context = new ExpressionCompilationContext(new CqlLanguage()); 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 CqlLinguist(context, CqlLanguage.Default, translator), translation, provider); } /// /// Find the expression of the specified type, either in the specified expression or parameters. /// private static Expression Resolve(Expression expression, IList 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 Execute(IStorageOperation operation) where TResult : IEntity { var result = Storage.Open().Query(new StorageContextArgs(operation)); if (result.IsCompleted) return result.Result; var r = result.GetAwaiter().GetResult(); return r; } public bool SupportsEntity(Type entityType) { return entityType.ImplementsInterface(typeof(ITableEntity<>)); } public IStorageOperation CreateOperation(TEntity entity) where TEntity : IEntity { var builder = new AggregatedCommandBuilder(); if (builder.Build(entity) is not StorageOperation operation) throw new NullReferenceException(nameof(StorageOperation)); return operation; } public IStorageReader OpenReader(IStorageOperation operation, IStorageConnection connection) { return new TableReader(operation, connection); } public IStorageWriter OpenWriter(IStorageOperation operation, IStorageConnection connection) { return new TableWriter(operation, connection); } public async Task Initialize() { await Task.CompletedTask; } }