From f423023653b37a786316075fd1970583d10db441 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Thu, 14 Mar 2024 22:42:18 -0500 Subject: [PATCH] More refactor --- .../SqlQueryParser.cs | 29 ++++++++----------- .../SqlQueryParserConfiguration.cs | 9 ------ .../Visitors/SqlQueryVisitorContext.cs | 2 ++ .../SqlQueryParserTests.cs | 19 +++++------- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/Foundatio.Parsers.SqlQueries/SqlQueryParser.cs b/src/Foundatio.Parsers.SqlQueries/SqlQueryParser.cs index d7d98317..6b427131 100644 --- a/src/Foundatio.Parsers.SqlQueries/SqlQueryParser.cs +++ b/src/Foundatio.Parsers.SqlQueries/SqlQueryParser.cs @@ -54,17 +54,14 @@ public override async Task ParseAsync(string query, IQueryVisitorCon } private static readonly ConcurrentDictionary> _entityFieldCache = new(); - - public async Task ValidateAsync(string query, IEntityType entityType) + public async Task ValidateAsync(string query, SqlQueryVisitorContext context) { - var context = await GetContextAsync(entityType); var node = await ParseAsync(query, context); return await ValidationVisitor.RunAsync(node, context); } - public async Task ToSqlAsync(string query, IEntityType entityType) + public async Task ToSqlAsync(string query, SqlQueryVisitorContext context) { - var context = await GetContextAsync(entityType); var node = await ParseAsync(query, context); var result = await ValidationVisitor.RunAsync(node, context); if (!result.IsValid) @@ -73,30 +70,28 @@ public async Task ToSqlAsync(string query, IEntityType entityType) return await GenerateSqlVisitor.RunAsync(node, context); } - private async Task GetContextAsync(IEntityType entityType) + public SqlQueryVisitorContext GetContext(IEntityType entityType) { if (!_entityFieldCache.TryGetValue(entityType, out var fields)) { fields = new List(); - AddFields(fields, entityType); - - if (Configuration.EntityTypeDynamicFieldResolver != null) - { - var dynamicFields = await Configuration.EntityTypeDynamicFieldResolver!.Invoke(entityType) ?? []; - fields.AddRange(dynamicFields); - } - + AddEntityFields(fields, entityType); _entityFieldCache.TryAdd(entityType, fields); } + var validationOptions = new QueryValidationOptions(); foreach (string field in fields.Select(f => f.Field)) validationOptions.AllowedFields.Add(field); Configuration.SetValidationOptions(validationOptions); - return new SqlQueryVisitorContext { Fields = fields }; + return new SqlQueryVisitorContext + { + Fields = fields, + ValidationOptions = validationOptions + }; } - private void AddFields(List fields, IEntityType entityType, List visited = null, string prefix = null) + private void AddEntityFields(List fields, IEntityType entityType, List visited = null, string prefix = null) { visited ??= []; if (visited.Contains(entityType)) @@ -123,7 +118,7 @@ private void AddFields(List fields, IEntityType entityType, Lis if (visited.Contains(nav.TargetEntityType)) continue; - AddFields(fields, nav.TargetEntityType, visited, prefix + nav.Name + "."); + AddEntityFields(fields, nav.TargetEntityType, visited, prefix + nav.Name + "."); } } diff --git a/src/Foundatio.Parsers.SqlQueries/SqlQueryParserConfiguration.cs b/src/Foundatio.Parsers.SqlQueries/SqlQueryParserConfiguration.cs index b2b5324f..8afbf701 100644 --- a/src/Foundatio.Parsers.SqlQueries/SqlQueryParserConfiguration.cs +++ b/src/Foundatio.Parsers.SqlQueries/SqlQueryParserConfiguration.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Foundatio.Parsers.LuceneQueries; using Foundatio.Parsers.LuceneQueries.Visitors; -using Foundatio.Parsers.SqlQueries.Visitors; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -23,10 +22,8 @@ public SqlQueryParserConfiguration() { public string[] DefaultFields { get; private set; } public QueryFieldResolver FieldResolver { get; private set; } - public EntityTypeDynamicFieldsResolver EntityTypeDynamicFieldResolver { get; private set; } public EntityTypePropertyFilter EntityTypePropertyFilter { get; private set; } = static _ => true; public IncludeResolver IncludeResolver { get; private set; } - //public ElasticMappingResolver MappingResolver { get; private set; } public QueryValidationOptions ValidationOptions { get; private set; } public ChainedQueryVisitor SortVisitor { get; } = new(); public ChainedQueryVisitor QueryVisitor { get; } = new(); @@ -44,11 +41,6 @@ public SqlQueryParserConfiguration SetDefaultFields(string[] fields) { return this; } - public SqlQueryParserConfiguration UseEntityTypeDynamicFieldResolver(EntityTypeDynamicFieldsResolver resolver) { - EntityTypeDynamicFieldResolver = resolver; - return this; - } - public SqlQueryParserConfiguration UseEntityTypePropertyFilter(EntityTypePropertyFilter filter) { EntityTypePropertyFilter = filter; return this; @@ -234,5 +226,4 @@ public SqlQueryParserConfiguration AddAggregationVisitorAfter(IChainableQuery #endregion } -public delegate Task> EntityTypeDynamicFieldsResolver(IEntityType entityType); public delegate bool EntityTypePropertyFilter(IProperty property); diff --git a/src/Foundatio.Parsers.SqlQueries/Visitors/SqlQueryVisitorContext.cs b/src/Foundatio.Parsers.SqlQueries/Visitors/SqlQueryVisitorContext.cs index b9ede004..cdeb51e8 100644 --- a/src/Foundatio.Parsers.SqlQueries/Visitors/SqlQueryVisitorContext.cs +++ b/src/Foundatio.Parsers.SqlQueries/Visitors/SqlQueryVisitorContext.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using System.Diagnostics; using Foundatio.Parsers.LuceneQueries.Visitors; +using Microsoft.EntityFrameworkCore.Metadata; namespace Foundatio.Parsers.SqlQueries.Visitors; public class SqlQueryVisitorContext : QueryVisitorContext, ISqlQueryVisitorContext { public List Fields { get; set; } + public IEntityType EntityType { get; set; } } [DebuggerDisplay("{Field} IsNumber: {IsNumber} IsDate: {IsDate} IsBoolean: {IsBoolean} Children: {Children?.Count}")] diff --git a/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs b/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs index 904a190c..fc802480 100644 --- a/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs +++ b/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs @@ -70,15 +70,6 @@ public async Task CanGenerateSql() }, ServiceLifetime.Scoped, ServiceLifetime.Singleton); var parser = new SqlQueryParser(); parser.Configuration.AddQueryVisitor(new DynamicFieldVisitor()); - parser.Configuration.UseEntityTypeDynamicFieldResolver(async entityType => - { - var dynamicFields = new List(); - if (entityType.ClrType == typeof(Employee)) - { - dynamicFields.Add(new EntityFieldInfo { Field = "age", IsNumber = true, Data = {{ "DataDefinitionId", 1 }}}); - } - return dynamicFields; - }); parser.Configuration.UseEntityTypePropertyFilter(p => { if (p.DeclaringType.ClrType == typeof(Company) && p.Name == "Description") @@ -120,19 +111,23 @@ public async Task CanGenerateSql() }); await db.SaveChangesAsync(); + var context = parser.GetContext(db.Employees.EntityType); + context.Fields.Add(new EntityFieldInfo { Field = "age", IsNumber = true, Data = {{ "DataDefinitionId", 1 }}}); + context.ValidationOptions.AllowedFields.Add("age"); + string sqlExpected = db.Employees.Where(e => e.Company.Name == "acme" && e.DataValues.Any(dv => dv.DataDefinitionId == 1 && dv.NumberValue == 30)).ToQueryString(); string sqlActual = db.Employees.Where("""company.name = "acme" AND DataValues.Any(DataDefinitionId = 1 AND NumberValue = 30) """).ToQueryString(); Assert.Equal(sqlExpected, sqlActual); - string sql = await parser.ToSqlAsync("company.name:acme age:30", db.Employees.EntityType); + string sql = await parser.ToSqlAsync("company.name:acme age:30", context); sqlActual = db.Employees.Where(sql).ToQueryString(); Assert.Equal(sqlExpected, sqlActual); var q = db.Employees.AsNoTracking(); - sql = await parser.ToSqlAsync("company.name:acme age:30", db.Employees.EntityType); + sql = await parser.ToSqlAsync("company.name:acme age:30", context); sqlActual = q.Where(sql, db.Employees).ToQueryString(); Assert.Equal(sqlExpected, sqlActual); - await Assert.ThrowsAsync(() => parser.ToSqlAsync("company.description:acme", db.Employees.EntityType)); + await Assert.ThrowsAsync(() => parser.ToSqlAsync("company.description:acme", context)); var employees = await db.Employees.Where(e => e.Title == "software developer" && e.DataValues.Any(dv => dv.DataDefinitionId == 1 && dv.NumberValue == 30)) .ToListAsync();