From b7bac1524b5d75f9a8f8be21b1c2a4488bd74117 Mon Sep 17 00:00:00 2001 From: countincognito Date: Tue, 25 Feb 2020 15:58:44 +0000 Subject: [PATCH 1/6] Add RenameEmptyParameterExpressionNames configuration. --- .../DynamicExpressionParser.cs | 4 +-- .../DynamicQueryableExtensions.cs | 8 ++--- .../ParameterExpressionHelper.cs | 33 +++++++++++++++++-- .../Parser/ExpressionParser.cs | 2 +- src/System.Linq.Dynamic.Core/ParsingConfig.cs | 15 +++++++++ .../DynamicExpressionParserTests.cs | 19 +++++++++++ 6 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs index c48e7a3f..6afb7e34 100644 --- a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs @@ -128,7 +128,7 @@ public static Expression> ParseLambda([CanBeNull] P { Check.NotEmpty(expression, nameof(expression)); - return (Expression>)ParseLambda(parsingConfig, createParameterCtor, new[] { ParameterExpressionHelper.CreateParameterExpression(typeof(T), string.Empty) }, typeof(TResult), expression, values); + return (Expression>)ParseLambda(parsingConfig, createParameterCtor, new[] { ParameterExpressionHelper.CreateParameterExpression(typeof(T), string.Empty, parsingConfig?.RenameEmptyParameterExpressionNames ?? false) }, typeof(TResult), expression, values); } /// @@ -205,7 +205,7 @@ public static LambdaExpression ParseLambda([CanBeNull] ParsingConfig parsingConf Check.NotNull(itType, nameof(itType)); Check.NotEmpty(expression, nameof(expression)); - return ParseLambda(parsingConfig, createParameterCtor, new[] { ParameterExpressionHelper.CreateParameterExpression(itType, string.Empty) }, resultType, expression, values); + return ParseLambda(parsingConfig, createParameterCtor, new[] { ParameterExpressionHelper.CreateParameterExpression(itType, string.Empty, parsingConfig?.RenameEmptyParameterExpressionNames ?? false) }, resultType, expression, values); } /// diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 31cbdb70..4870746c 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -1294,7 +1294,7 @@ public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNu Check.NotNull(config, nameof(config)); Check.NotEmpty(ordering, nameof(ordering)); - ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty) }; + ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty, config.RenameEmptyParameterExpressionNames) }; ExpressionParser parser = new ExpressionParser(parameters, ordering, args, config); IList dynamicOrderings = parser.ParseOrdering(); @@ -1767,8 +1767,8 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] sourceSelectLambda = Expression.Lambda(sourceLambdaDelegateType, sourceSelectLambda.Body, sourceSelectLambda.Parameters); //we have to create additional lambda for result selection - ParameterExpression xParameter = ParameterExpressionHelper.CreateParameterExpression(source.ElementType, collectionParameterName); - ParameterExpression yParameter = ParameterExpressionHelper.CreateParameterExpression(sourceLambdaResultType, resultParameterName); + ParameterExpression xParameter = ParameterExpressionHelper.CreateParameterExpression(source.ElementType, collectionParameterName, config.RenameEmptyParameterExpressionNames); + ParameterExpression yParameter = ParameterExpressionHelper.CreateParameterExpression(sourceLambdaResultType, resultParameterName, config.RenameEmptyParameterExpressionNames); LambdaExpression resultSelectLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, new[] { xParameter, yParameter }, null, resultSelector, resultSelectorArgs); Type resultLambdaResultType = resultSelectLambda.Body.Type; @@ -2198,7 +2198,7 @@ public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source, Check.NotNull(config, nameof(config)); Check.NotEmpty(ordering, nameof(ordering)); - ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty) }; + ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty, config.RenameEmptyParameterExpressionNames) }; ExpressionParser parser = new ExpressionParser(parameters, ordering, args, config); IList dynamicOrderings = parser.ParseOrdering(forceThenBy: true); diff --git a/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs b/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs index 02150381..0c2e7b89 100644 --- a/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs @@ -4,9 +4,38 @@ namespace System.Linq.Dynamic.Core { internal static class ParameterExpressionHelper { - public static ParameterExpression CreateParameterExpression(Type type, string name) + internal const int RandomWordLength = 6; + + public static ParameterExpression CreateParameterExpression(Type type, string name, bool renameEmpty = false) + { + string paramName = name; + if (renameEmpty && IsNullOrWhiteSpace(paramName)) + { + paramName = GenerateRandomWord(); + } + return Expression.Parameter(type, paramName); + } + + internal static bool IsNullOrWhiteSpace(string value) + { + if (value == null) + { + return true; + } + for (int i = 0; i < value.Length; i++) + { + if (!char.IsWhiteSpace(value[i])) + { + return false; + } + } + return true; + } + + internal static string GenerateRandomWord() { - return Expression.Parameter(type, name); + const int diff = 'A' - '0'; + return string.Concat(Guid.NewGuid().ToString(@"N").Select(c => (char)(c + diff)).Take(RandomWordLength)).ToLower(); } } } diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 98d1053c..404fa5ad 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1716,7 +1716,7 @@ Expression ParseAggregate(Expression instance, Type elementType, string methodNa var oldParent = _parent; ParameterExpression outerIt = _it; - ParameterExpression innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty); + ParameterExpression innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames); _parent = _it; diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index 4ad9bea7..1ee0fc8a 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -22,6 +22,14 @@ public class ParsingConfig EvaluateGroupByAtDatabase = true }; + /// + /// Default ParsingConfig for CosmosDb + /// + public static ParsingConfig DefaultCosmosDb { get; } = new ParsingConfig + { + RenameEmptyParameterExpressionNames = true + }; + private IDynamicLinkCustomTypeProvider _customTypeProvider; private IExpressionPromoter _expressionPromoter; @@ -134,6 +142,13 @@ public IQueryableAnalyzer QueryableAnalyzer /// public bool RenameParameterExpression { get; set; } = false; + /// + /// Prevents any System.Linq.Expressions.ParameterExpression.Name from being empty. + /// + /// Default value is false. + /// + public bool RenameEmptyParameterExpressionNames { get; set; } = false; + /// /// By default when a member is not found in a type and the type has a string based index accessor it will be parsed as an index accessor. Use /// this flag to disable this behaviour and have parsing fail when parsing an expression diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index dee9d5c0..8c0a3b38 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -1009,6 +1009,25 @@ public void DynamicExpressionParser_ParseLambda_RenameParameterExpression(string Check.That(result).IsEqualTo(expected); } + [Theory] + [InlineData("c => c.Age == 8", "[a-z]{6} =\\> \\([a-z]{6}\\.Age == 8\\)")] + [InlineData("c => c.Name == \"test\"", "[a-z]{6} =\\> \\([a-z]{6}\\.Name == \"test\"\\)")] + public void DynamicExpressionParser_ParseLambda_RenameEmptyParameterExpressionNames(string expressionAsString, string expected) + { + // Arrange + var config = new ParsingConfig + { + RenameEmptyParameterExpressionNames = true + }; + + // Act + var expression = DynamicExpressionParser.ParseLambda(config, true, expressionAsString); + string result = expression.ToString(); + + // Assert + Check.That(result).Matches(expected); + } + [Theory] [InlineData(@"p0.Equals(""Testing"", 3)", "testinG", true)] [InlineData(@"p0.Equals(""Testing"", StringComparison.InvariantCultureIgnoreCase)", "testinG", true)] From 238df2132c9463cdd1356e42221be0ddfdb34cd3 Mon Sep 17 00:00:00 2001 From: countincognito Date: Tue, 25 Feb 2020 16:57:26 +0000 Subject: [PATCH 2/6] Add ConsoleApp_netcore2.1_CosmosDb test console. --- System.Linq.Dynamic.Core.sln | 19 ++ .../ConsoleApp_netcore2.1_CosmosDb.csproj | 18 ++ .../Entities/Entity.cs | 11 + .../Entities/EntityData.cs | 11 + .../Entities/EntityReading.cs | 12 + .../Entities/EntityReadingValue.cs | 10 + .../ConsoleApp_netcore2.1_CosmosDb/Program.cs | 255 ++++++++++++++++++ 7 files changed, 336 insertions(+) create mode 100644 src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj create mode 100644 src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs create mode 100644 src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs create mode 100644 src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReading.cs create mode 100644 src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReadingValue.cs create mode 100644 src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index d3cf0f7c..190f5800 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -73,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp.EntityFrameworkC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp_netcore3.1_EF3.1", "src-console\ConsoleAppEF3.1\ConsoleApp_netcore3.1_EF3.1.csproj", "{6D21EBF2-C92D-4AE0-9BC3-47C63928F88A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp_netcore2.1_CosmosDb", "src-console\ConsoleApp_netcore2.1_CosmosDb\ConsoleApp_netcore2.1_CosmosDb.csproj", "{D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -439,6 +441,22 @@ Global {6D21EBF2-C92D-4AE0-9BC3-47C63928F88A}.Release|x64.Build.0 = Release|Any CPU {6D21EBF2-C92D-4AE0-9BC3-47C63928F88A}.Release|x86.ActiveCfg = Release|Any CPU {6D21EBF2-C92D-4AE0-9BC3-47C63928F88A}.Release|x86.Build.0 = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|ARM.Build.0 = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|x64.ActiveCfg = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|x64.Build.0 = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|x86.ActiveCfg = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Debug|x86.Build.0 = Debug|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|Any CPU.Build.0 = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|ARM.ActiveCfg = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|ARM.Build.0 = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x64.ActiveCfg = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x64.Build.0 = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x86.ActiveCfg = Release|Any CPU + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -466,6 +484,7 @@ Global {D3804228-91F4-4502-9595-39584EA20000} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F} {0077F262-D69B-44D2-8A7C-87D8D19022A6} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {6D21EBF2-C92D-4AE0-9BC3-47C63928F88A} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} + {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj b/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj new file mode 100644 index 00000000..37121bac --- /dev/null +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp2.1 + ConsoleAppCosmosDb + ConsoleAppCosmosDb + + + + + + + + + + + diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs new file mode 100644 index 00000000..f970d1de --- /dev/null +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs @@ -0,0 +1,11 @@ +using System; + +namespace ConsoleAppCosmosDb.Entities +{ + [Serializable] + public class Entity + { + public Guid Id { get; set; } + public EntityData Data { get; set; } + } +} diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs new file mode 100644 index 00000000..895655bd --- /dev/null +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace ConsoleAppCosmosDb.Entities +{ + [Serializable] + public class EntityData + { + public IList Readings { get; set; } + } +} diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReading.cs b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReading.cs new file mode 100644 index 00000000..63627426 --- /dev/null +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReading.cs @@ -0,0 +1,12 @@ +using System; + +namespace ConsoleAppCosmosDb.Entities +{ + [Serializable] + public class EntityReading + { + public string Status { get; set; } + public int Weight { get; set; } + public EntityReadingValue Value { get; set; } + } +} diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReadingValue.cs b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReadingValue.cs new file mode 100644 index 00000000..97038cd1 --- /dev/null +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReadingValue.cs @@ -0,0 +1,10 @@ +using System; + +namespace ConsoleAppCosmosDb.Entities +{ + [Serializable] + public class EntityReadingValue + { + public int Weight { get; set; } + } +} diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs b/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs new file mode 100644 index 00000000..10d7f391 --- /dev/null +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs @@ -0,0 +1,255 @@ +using ConsoleAppCosmosDb.Entities; +using Microsoft.Azure.Documents; +using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Documents.Linq; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; + +namespace ConsoleAppCosmosDb +{ + class Program + { + // Designed for use with the Cosmos DB Emulator + // https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator + private static readonly string _DatabaseName = "samples-db"; + private static readonly string _CollectionName = "samples-col"; + private static readonly string _EndpointUrl = "https://localhost:8081"; + private static readonly string _AuthorizationKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; + private static DocumentClient _Client; + + static void Main(string[] args) + { + using (_Client = new DocumentClient(new Uri(_EndpointUrl), _AuthorizationKey, + new ConnectionPolicy { ConnectionMode = ConnectionMode.Gateway, ConnectionProtocol = Protocol.Https })) + { + RunDemoAsync(_DatabaseName, _CollectionName).Wait(); + } + } + + private static async Task RunDemoAsync(string databaseId, string collectionId) + { + Database database = await GetOrCreateDatabaseAsync(databaseId); + DocumentCollection collection = await GetOrCreateCollectionAsync(databaseId, collectionId); + Uri collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); + + await PopulateCollectionAsync(collectionUri); + + + Console.WriteLine("TESTING ANY()"); + Console.WriteLine(); + + string anyReadingsInvalid = @"Data.Readings.Any( x => x.Status == @0 )"; + object[] anyReadingsInvalidParams = new[] { @"invalid" }; + + QueryCollection(collectionUri, ParsingConfig.Default, nameof(ParsingConfig.Default), anyReadingsInvalid, anyReadingsInvalidParams, @"FAIL"); + + QueryCollection(collectionUri, ParsingConfig.DefaultCosmosDb, nameof(ParsingConfig.DefaultCosmosDb), anyReadingsInvalid, anyReadingsInvalidParams, @"SUCCEED"); + + Console.WriteLine(); + Console.WriteLine(); + + Console.WriteLine("TESTING SUM()"); + Console.WriteLine(); + + string sumReadingsInvalid = @"Data.Readings.Sum( x => x.Weight ) <= @0"; + object[] sumReadingsInvalidParams = new[] { @"5" }; + + QueryCollection(collectionUri, ParsingConfig.Default, nameof(ParsingConfig.Default), sumReadingsInvalid, sumReadingsInvalidParams, @"FAIL"); + + QueryCollection(collectionUri, ParsingConfig.DefaultCosmosDb, nameof(ParsingConfig.DefaultCosmosDb), sumReadingsInvalid, sumReadingsInvalidParams, @"SUCCEED"); + + + await DeleteDatabaseAsync(); + } + + private static async Task GetOrCreateDatabaseAsync(string databaseId) + { + return await _Client.CreateDatabaseIfNotExistsAsync(new Database { Id = databaseId }); + } + + private static async Task GetOrCreateCollectionAsync(string databaseId, string collectionId) + { + DocumentCollection collectionDefinition = new DocumentCollection(); + collectionDefinition.Id = collectionId; + collectionDefinition.IndexingPolicy = new IndexingPolicy(new RangeIndex(DataType.String) { Precision = -1 }); + collectionDefinition.PartitionKey.Paths.Add("/Id"); + + return await _Client.CreateDocumentCollectionIfNotExistsAsync( + UriFactory.CreateDatabaseUri(databaseId), + collectionDefinition, + new RequestOptions { OfferThroughput = 400 }); + } + + private static async Task PopulateCollectionAsync(Uri collectionUri) + { + var entities = new List() + { + new Entity + { + Id = Guid.NewGuid(), + Data = new EntityData + { + Readings = new List + { + new EntityReading + { + Status = "valid", + Weight = 10, + }, + new EntityReading + { + Status = "invalid", + Weight = 2, + }, + }, + }, + }, + new Entity + { + Id = Guid.NewGuid(), + Data = new EntityData + { + Readings = new List + { + new EntityReading + { + Status = "valid", + Weight = 0, + Value = new EntityReadingValue + { + Weight = 0, + }, + }, + }, + }, + }, + new Entity + { + Id = Guid.NewGuid(), + Data = new EntityData + { + Readings = new List + { + new EntityReading + { + Status = "Valid", + Weight = 12, + }, + }, + }, + }, + new Entity + { + Id = Guid.NewGuid(), + Data = new EntityData + { + Readings = new List + { + new EntityReading + { + Status = "VALID", + Weight = 9, + Value = new EntityReadingValue + { + Weight = 0, + }, + }, + }, + }, + }, + new Entity + { + Id = Guid.NewGuid(), + Data = new EntityData + { + Readings = new List + { + new EntityReading + { + Status = "invalid", + Weight = 5, + }, + }, + }, + }, + new Entity + { + Id = Guid.NewGuid(), + Data = new EntityData + { + Readings = new List + { + new EntityReading + { + Status = "invalid", + Weight = 0, + Value = new EntityReadingValue + { + Weight = 3, + }, + }, + new EntityReading + { + Status = "invalid", + Weight = 8, + Value = new EntityReadingValue + { + Weight = 8, + }, + }, + }, + }, + }, + }; + + foreach (var entity in entities) + { + await _Client.UpsertDocumentAsync(collectionUri, entity); + } + } + + private static void QueryCollection(Uri collectionUri, ParsingConfig config, string configName, string predicate, object[] parameters, string successOrFailure) + { + try + { + IQueryable entities = _Client.CreateDocumentQuery(collectionUri); + + IQueryable results = entities.Where(config, predicate, parameters); + + string querySqlJson = results.AsDocumentQuery().ToString(); + JObject querySqlJsonObject = JObject.Parse(querySqlJson); + string querySql = (string)querySqlJsonObject["query"]; + + Console.WriteLine($"Config {configName} produces the following SQL:"); + Console.WriteLine(); + Console.WriteLine($"**** {querySql} ****"); + Console.WriteLine(); + Console.WriteLine($"This request will {successOrFailure}"); + Console.WriteLine(); + + var querySqlSpec = new SqlQuerySpec(querySql); + + results = _Client.CreateDocumentQuery(collectionUri, querySqlSpec, new FeedOptions() { EnableCrossPartitionQuery = true }); + IList output = results.ToList(); + + Console.WriteLine($"Successfully returned {output.Count} entities"); + Console.WriteLine(); + } + catch (Exception ex) + { + Console.WriteLine($"Exception thrown: {ex.Message}"); + Console.WriteLine(); + } + Console.WriteLine(); + } + + private static async Task DeleteDatabaseAsync() + { + await _Client.DeleteDatabaseAsync(UriFactory.CreateDatabaseUri(_DatabaseName)); + } + } +} From 766d020714626e491fad203c872e80a74acf2bc1 Mon Sep 17 00:00:00 2001 From: countincognito Date: Tue, 25 Feb 2020 21:42:06 +0000 Subject: [PATCH 3/6] Add ParameterExpressionHelper unit tests. CodeFactor feedback. --- .../ConsoleApp_netcore2.1_CosmosDb/Program.cs | 2 - .../Parser/ParameterExpressionHelperTests.cs | 117 ++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs b/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs index 10d7f391..9e717eab 100644 --- a/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs @@ -38,7 +38,6 @@ private static async Task RunDemoAsync(string databaseId, string collectionId) await PopulateCollectionAsync(collectionUri); - Console.WriteLine("TESTING ANY()"); Console.WriteLine(); @@ -62,7 +61,6 @@ private static async Task RunDemoAsync(string databaseId, string collectionId) QueryCollection(collectionUri, ParsingConfig.DefaultCosmosDb, nameof(ParsingConfig.DefaultCosmosDb), sumReadingsInvalid, sumReadingsInvalidParams, @"SUCCEED"); - await DeleteDatabaseAsync(); } diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs new file mode 100644 index 00000000..c8734d6a --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs @@ -0,0 +1,117 @@ +using NFluent; +using System.Linq.Dynamic.Core.Parser; +using System.Linq.Expressions; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests.Parser +{ + public class ParameterExpressionHelperTests + { + [Theory] + [InlineData(typeof(int), "it", "x", "x", false)] + [InlineData(typeof(int), "it == 1", @"\(x == 1\)", "x", false)] + [InlineData(typeof(int), "it eq 1", @"\(x == 1\)", "x", false)] + [InlineData(typeof(int), "it equal 1", @"\(x == 1\)", "x", false)] + [InlineData(typeof(int), "it != 1", @"\(x != 1\)", "x", false)] + [InlineData(typeof(int), "it ne 1", @"\(x != 1\)", "x", false)] + [InlineData(typeof(int), "it neq 1", @"\(x != 1\)", "x", false)] + [InlineData(typeof(int), "it notequal 1", @"\(x != 1\)", "x", false)] + [InlineData(typeof(int), "it lt 1", @"\(x < 1\)", "x", false)] + [InlineData(typeof(int), "it LessThan 1", @"\(x < 1\)", "x", false)] + [InlineData(typeof(int), "it le 1", @"\(x <= 1\)", "x", false)] + [InlineData(typeof(int), "it LessThanEqual 1", @"\(x <= 1\)", "x", false)] + [InlineData(typeof(int), "it gt 1", @"\(x > 1\)", "x", false)] + [InlineData(typeof(int), "it GreaterThan 1", @"\(x > 1\)", "x", false)] + [InlineData(typeof(int), "it ge 1", @"\(x >= 1\)", "x", false)] + [InlineData(typeof(int), "it GreaterThanEqual 1", @"\(x >= 1\)", "x", false)] + + [InlineData(typeof(int), "it", "x", "x", true)] + [InlineData(typeof(int), "it == 1", @"\(x == 1\)", "x", true)] + [InlineData(typeof(int), "it eq 1", @"\(x == 1\)", "x", true)] + [InlineData(typeof(int), "it equal 1", @"\(x == 1\)", "x", true)] + [InlineData(typeof(int), "it != 1", @"\(x != 1\)", "x", true)] + [InlineData(typeof(int), "it ne 1", @"\(x != 1\)", "x", true)] + [InlineData(typeof(int), "it neq 1", @"\(x != 1\)", "x", true)] + [InlineData(typeof(int), "it notequal 1", @"\(x != 1\)", "x", true)] + [InlineData(typeof(int), "it lt 1", @"\(x < 1\)", "x", true)] + [InlineData(typeof(int), "it LessThan 1", @"\(x < 1\)", "x", true)] + [InlineData(typeof(int), "it le 1", @"\(x <= 1\)", "x", true)] + [InlineData(typeof(int), "it LessThanEqual 1", @"\(x <= 1\)", "x", true)] + [InlineData(typeof(int), "it gt 1", @"\(x > 1\)", "x", true)] + [InlineData(typeof(int), "it GreaterThan 1", @"\(x > 1\)", "x", true)] + [InlineData(typeof(int), "it ge 1", @"\(x >= 1\)", "x", true)] + [InlineData(typeof(int), "it GreaterThanEqual 1", @"\(x >= 1\)", "x", true)] + + [InlineData(typeof(int), "it", "Param_0", "", false)] + [InlineData(typeof(int), "it == 1", @"\(Param_0 == 1\)", "", false)] + [InlineData(typeof(int), "it eq 1", @"\(Param_0 == 1\)", "", false)] + [InlineData(typeof(int), "it equal 1", @"\(Param_0 == 1\)", "", false)] + [InlineData(typeof(int), "it != 1", @"\(Param_0 != 1\)", "", false)] + [InlineData(typeof(int), "it ne 1", @"\(Param_0 != 1\)", "", false)] + [InlineData(typeof(int), "it neq 1", @"\(Param_0 != 1\)", "", false)] + [InlineData(typeof(int), "it notequal 1", @"\(Param_0 != 1\)", "", false)] + [InlineData(typeof(int), "it lt 1", @"\(Param_0 < 1\)", "", false)] + [InlineData(typeof(int), "it LessThan 1", @"\(Param_0 < 1\)", "", false)] + [InlineData(typeof(int), "it le 1", @"\(Param_0 <= 1\)", "", false)] + [InlineData(typeof(int), "it LessThanEqual 1", @"\(Param_0 <= 1\)", "", false)] + [InlineData(typeof(int), "it gt 1", @"\(Param_0 > 1\)", "", false)] + [InlineData(typeof(int), "it GreaterThan 1", @"\(Param_0 > 1\)", "", false)] + [InlineData(typeof(int), "it ge 1", @"\(Param_0 >= 1\)", "", false)] + [InlineData(typeof(int), "it GreaterThanEqual 1", @"\(Param_0 >= 1\)", "", false)] + + [InlineData(typeof(int), "it", "[a-z]{6}", "", true)] + [InlineData(typeof(int), "it == 1", @"\([a-z]{6} == 1\)", "", true)] + [InlineData(typeof(int), "it eq 1", @"\([a-z]{6} == 1\)", "", true)] + [InlineData(typeof(int), "it equal 1", @"\([a-z]{6} == 1\)", "", true)] + [InlineData(typeof(int), "it != 1", @"\([a-z]{6} != 1\)", "", true)] + [InlineData(typeof(int), "it ne 1", @"\([a-z]{6} != 1\)", "", true)] + [InlineData(typeof(int), "it neq 1", @"\([a-z]{6} != 1\)", "", true)] + [InlineData(typeof(int), "it notequal 1", @"\([a-z]{6} != 1\)", "", true)] + [InlineData(typeof(int), "it lt 1", @"\([a-z]{6} < 1\)", "", true)] + [InlineData(typeof(int), "it LessThan 1", @"\([a-z]{6} < 1\)", "", true)] + [InlineData(typeof(int), "it le 1", @"\([a-z]{6} <= 1\)", "", true)] + [InlineData(typeof(int), "it LessThanEqual 1", @"\([a-z]{6} <= 1\)", "", true)] + [InlineData(typeof(int), "it gt 1", @"\([a-z]{6} > 1\)", "", true)] + [InlineData(typeof(int), "it GreaterThan 1", @"\([a-z]{6} > 1\)", "", true)] + [InlineData(typeof(int), "it ge 1", @"\([a-z]{6} >= 1\)", "", true)] + [InlineData(typeof(int), "it GreaterThanEqual 1", @"\([a-z]{6} >= 1\)", "", true)] + + [InlineData(typeof(bool), "it || true", @"\(x OrElse True\)", "x", false)] + [InlineData(typeof(bool), "it or true", @"\(x OrElse True\)", "x", false)] + [InlineData(typeof(bool), "it OrElse true", @"\(x OrElse True\)", "x", false)] + [InlineData(typeof(bool), "it || true", @"\(x OrElse True\)", "x", true)] + [InlineData(typeof(bool), "it or true", @"\(x OrElse True\)", "x", true)] + [InlineData(typeof(bool), "it OrElse true", @"\(x OrElse True\)", "x", true)] + [InlineData(typeof(bool), "it || true", @"\(Param_0 OrElse True\)", "", false)] + [InlineData(typeof(bool), "it or true", @"\(Param_0 OrElse True\)", "", false)] + [InlineData(typeof(bool), "it OrElse true", @"\(Param_0 OrElse True\)", "", false)] + [InlineData(typeof(bool), "it || true", @"\([a-z]{6} OrElse True\)", "", true)] + [InlineData(typeof(bool), "it or true", @"\([a-z]{6} OrElse True\)", "", true)] + [InlineData(typeof(bool), "it OrElse true", @"\([a-z]{6} OrElse True\)", "", true)] + + [InlineData(typeof(bool), "it && true", @"\(x AndAlso True\)", "x", false)] + [InlineData(typeof(bool), "it and true", @"\(x AndAlso True\)", "x", false)] + [InlineData(typeof(bool), "it AndAlso true", @"\(x AndAlso True\)", "x", false)] + [InlineData(typeof(bool), "it && true", @"\(x AndAlso True\)", "x", true)] + [InlineData(typeof(bool), "it and true", @"\(x AndAlso True\)", "x", true)] + [InlineData(typeof(bool), "it AndAlso true", @"\(x AndAlso True\)", "x", true)] + [InlineData(typeof(bool), "it && true", @"\(Param_0 AndAlso True\)", "", false)] + [InlineData(typeof(bool), "it and true", @"\(Param_0 AndAlso True\)", "", false)] + [InlineData(typeof(bool), "it AndAlso true", @"\(Param_0 AndAlso True\)", "", false)] + [InlineData(typeof(bool), "it && true", @"\([a-z]{6} AndAlso True\)", "", true)] + [InlineData(typeof(bool), "it and true", @"\([a-z]{6} AndAlso True\)", "", true)] + [InlineData(typeof(bool), "it AndAlso true", @"\([a-z]{6} AndAlso True\)", "", true)] + public void ParameterExpressionHelper_CreateParameterExpression(Type type, string expression, string result, string substitute, bool renameEmpty) + { + // Arrange + ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(type, substitute, renameEmpty) }; + var sut = new ExpressionParser(parameters, expression, null, null); + + // Act + var parsedExpression = sut.Parse(null).ToString(); + + // Assert + Check.That(parsedExpression).Matches(result); + } + } +} From 6be8181714064591a057adf840e513ea4a848f10 Mon Sep 17 00:00:00 2001 From: countincognito Date: Tue, 25 Feb 2020 21:56:01 +0000 Subject: [PATCH 4/6] More unit tests for ParameterExpressionHelper. --- .../Parser/ParameterExpressionHelperTests.cs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs index c8734d6a..fe86298c 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs @@ -101,7 +101,7 @@ public class ParameterExpressionHelperTests [InlineData(typeof(bool), "it && true", @"\([a-z]{6} AndAlso True\)", "", true)] [InlineData(typeof(bool), "it and true", @"\([a-z]{6} AndAlso True\)", "", true)] [InlineData(typeof(bool), "it AndAlso true", @"\([a-z]{6} AndAlso True\)", "", true)] - public void ParameterExpressionHelper_CreateParameterExpression(Type type, string expression, string result, string substitute, bool renameEmpty) + public void ParameterExpressionHelper_CreateParameterExpression(Type type, string expression, string expectedResult, string substitute, bool renameEmpty) { // Arrange ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(type, substitute, renameEmpty) }; @@ -111,7 +111,44 @@ public void ParameterExpressionHelper_CreateParameterExpression(Type type, strin var parsedExpression = sut.Parse(null).ToString(); // Assert - Check.That(parsedExpression).Matches(result); + Check.That(parsedExpression).Matches(expectedResult); + } + + [Theory] + [InlineData(null, true)] + [InlineData("", true)] + [InlineData(" ", true)] + [InlineData(" ", true)] + [InlineData(" ", true)] + [InlineData(" ", true)] + [InlineData("\t", true)] + [InlineData("\n", true)] + [InlineData("x", false)] + [InlineData("xx", false)] + [InlineData("xxx", false)] + [InlineData("xxxx", false)] + [InlineData("!", false)] + [InlineData("\"", false)] + [InlineData("$", false)] + [InlineData("_", false)] + [InlineData("-", false)] + public void ParameterExpressionHelper_IsNullOrWhiteSpace(string input, bool expectedResult) + { + // Arrange and Act + bool result = ParameterExpressionHelper.IsNullOrWhiteSpace(input); + + // Assert + Check.That(result).IsEqualTo(expectedResult); + } + + [Fact] + public void ParameterExpressionHelper_GenerateRandomWord() + { + // Arrange and Act + string result = ParameterExpressionHelper.GenerateRandomWord(); + + // Assert + Check.That(result).Matches(@"[a-z]{6}"); } } } From df568c1eeb2f38e9239d79d62080b5994d214af0 Mon Sep 17 00:00:00 2001 From: countincognito Date: Wed, 26 Feb 2020 20:01:19 +0000 Subject: [PATCH 5/6] Code review feedback. --- System.Linq.Dynamic.Core.sln | 21 ++++- .../ConsoleApp_net452_CosmosDb/App.config | 6 ++ .../ConsoleApp_net452_CosmosDb.csproj | 82 +++++++++++++++++++ .../Entities/Entity.cs | 0 .../Entities/EntityData.cs | 0 .../Entities/EntityReading.cs | 0 .../Entities/EntityReadingValue.cs | 0 .../Program.cs | 0 .../Properties/AssemblyInfo.cs | 36 ++++++++ .../packages.config | 6 ++ .../ConsoleApp_netcore2.1_CosmosDb.csproj | 15 +++- .../ParameterExpressionHelper.cs | 8 +- src/System.Linq.Dynamic.Core/ParsingConfig.cs | 2 +- .../DynamicExpressionParserTests.cs | 4 +- .../Parser/ParameterExpressionHelperTests.cs | 48 +++++------ 15 files changed, 196 insertions(+), 32 deletions(-) create mode 100644 src-console/ConsoleApp_net452_CosmosDb/App.config create mode 100644 src-console/ConsoleApp_net452_CosmosDb/ConsoleApp_net452_CosmosDb.csproj rename src-console/{ConsoleApp_netcore2.1_CosmosDb => ConsoleApp_net452_CosmosDb}/Entities/Entity.cs (100%) rename src-console/{ConsoleApp_netcore2.1_CosmosDb => ConsoleApp_net452_CosmosDb}/Entities/EntityData.cs (100%) rename src-console/{ConsoleApp_netcore2.1_CosmosDb => ConsoleApp_net452_CosmosDb}/Entities/EntityReading.cs (100%) rename src-console/{ConsoleApp_netcore2.1_CosmosDb => ConsoleApp_net452_CosmosDb}/Entities/EntityReadingValue.cs (100%) rename src-console/{ConsoleApp_netcore2.1_CosmosDb => ConsoleApp_net452_CosmosDb}/Program.cs (100%) create mode 100644 src-console/ConsoleApp_net452_CosmosDb/Properties/AssemblyInfo.cs create mode 100644 src-console/ConsoleApp_net452_CosmosDb/packages.config diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index 190f5800..03dd9b66 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -73,7 +73,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp.EntityFrameworkC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp_netcore3.1_EF3.1", "src-console\ConsoleAppEF3.1\ConsoleApp_netcore3.1_EF3.1.csproj", "{6D21EBF2-C92D-4AE0-9BC3-47C63928F88A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp_netcore2.1_CosmosDb", "src-console\ConsoleApp_netcore2.1_CosmosDb\ConsoleApp_netcore2.1_CosmosDb.csproj", "{D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp_netcore2.1_CosmosDb", "src-console\ConsoleApp_netcore2.1_CosmosDb\ConsoleApp_netcore2.1_CosmosDb.csproj", "{D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp_net452_CosmosDb", "src-console\ConsoleApp_net452_CosmosDb\ConsoleApp_net452_CosmosDb.csproj", "{0034821E-740D-4553-821B-14CE9213C43C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -457,6 +459,22 @@ Global {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x64.Build.0 = Release|Any CPU {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x86.ActiveCfg = Release|Any CPU {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB}.Release|x86.Build.0 = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|ARM.Build.0 = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|x64.ActiveCfg = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|x64.Build.0 = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|x86.ActiveCfg = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Debug|x86.Build.0 = Debug|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|Any CPU.Build.0 = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|ARM.ActiveCfg = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|ARM.Build.0 = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|x64.ActiveCfg = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|x64.Build.0 = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|x86.ActiveCfg = Release|Any CPU + {0034821E-740D-4553-821B-14CE9213C43C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -485,6 +503,7 @@ Global {0077F262-D69B-44D2-8A7C-87D8D19022A6} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {6D21EBF2-C92D-4AE0-9BC3-47C63928F88A} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {D160E2CF-A7E1-4DDE-9AB8-8CFB0087DCEB} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} + {0034821E-740D-4553-821B-14CE9213C43C} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleApp_net452_CosmosDb/App.config b/src-console/ConsoleApp_net452_CosmosDb/App.config new file mode 100644 index 00000000..88fa4027 --- /dev/null +++ b/src-console/ConsoleApp_net452_CosmosDb/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src-console/ConsoleApp_net452_CosmosDb/ConsoleApp_net452_CosmosDb.csproj b/src-console/ConsoleApp_net452_CosmosDb/ConsoleApp_net452_CosmosDb.csproj new file mode 100644 index 00000000..c49d2a4a --- /dev/null +++ b/src-console/ConsoleApp_net452_CosmosDb/ConsoleApp_net452_CosmosDb.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {0034821E-740D-4553-821B-14CE9213C43C} + Exe + ConsoleAppCosmosDb + ConsoleAppCosmosDb + v4.5.2 + 512 + true + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + ConsoleAppCosmosDb.Program + + + + ..\..\packages\Microsoft.Azure.DocumentDB.2.5.1\lib\net45\Microsoft.Azure.Documents.Client.dll + + + ..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + {d3804228-91f4-4502-9595-39584e510002} + System.Linq.Dynamic.Core + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs b/src-console/ConsoleApp_net452_CosmosDb/Entities/Entity.cs similarity index 100% rename from src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs rename to src-console/ConsoleApp_net452_CosmosDb/Entities/Entity.cs diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs b/src-console/ConsoleApp_net452_CosmosDb/Entities/EntityData.cs similarity index 100% rename from src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs rename to src-console/ConsoleApp_net452_CosmosDb/Entities/EntityData.cs diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReading.cs b/src-console/ConsoleApp_net452_CosmosDb/Entities/EntityReading.cs similarity index 100% rename from src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReading.cs rename to src-console/ConsoleApp_net452_CosmosDb/Entities/EntityReading.cs diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReadingValue.cs b/src-console/ConsoleApp_net452_CosmosDb/Entities/EntityReadingValue.cs similarity index 100% rename from src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityReadingValue.cs rename to src-console/ConsoleApp_net452_CosmosDb/Entities/EntityReadingValue.cs diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs b/src-console/ConsoleApp_net452_CosmosDb/Program.cs similarity index 100% rename from src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs rename to src-console/ConsoleApp_net452_CosmosDb/Program.cs diff --git a/src-console/ConsoleApp_net452_CosmosDb/Properties/AssemblyInfo.cs b/src-console/ConsoleApp_net452_CosmosDb/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..0d8968fc --- /dev/null +++ b/src-console/ConsoleApp_net452_CosmosDb/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ConsoleApp_net452_CosmosDb")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ConsoleApp_net452_CosmosDb")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0034821e-740d-4553-821b-14ce9213c43c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src-console/ConsoleApp_net452_CosmosDb/packages.config b/src-console/ConsoleApp_net452_CosmosDb/packages.config new file mode 100644 index 00000000..5c4b4dc8 --- /dev/null +++ b/src-console/ConsoleApp_net452_CosmosDb/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj b/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj index 37121bac..44d8a5e0 100644 --- a/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj +++ b/src-console/ConsoleApp_netcore2.1_CosmosDb/ConsoleApp_netcore2.1_CosmosDb.csproj @@ -5,14 +5,27 @@ netcoreapp2.1 ConsoleAppCosmosDb ConsoleAppCosmosDb + ConsoleAppCosmosDb.Program + + + + + + + + - + + + + + diff --git a/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs b/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs index 0c2e7b89..377ed16d 100644 --- a/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs @@ -4,8 +4,6 @@ namespace System.Linq.Dynamic.Core { internal static class ParameterExpressionHelper { - internal const int RandomWordLength = 6; - public static ParameterExpression CreateParameterExpression(Type type, string name, bool renameEmpty = false) { string paramName = name; @@ -32,10 +30,14 @@ internal static bool IsNullOrWhiteSpace(string value) return true; } + /// + /// Generates a random 16 character word derived from a Guid value. + /// internal static string GenerateRandomWord() { + const int wordLength = 16; const int diff = 'A' - '0'; - return string.Concat(Guid.NewGuid().ToString(@"N").Select(c => (char)(c + diff)).Take(RandomWordLength)).ToLower(); + return string.Concat(Guid.NewGuid().ToString(@"N").Select(c => (char)(c + diff)).Take(wordLength)).ToLower(); } } } diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index 1ee0fc8a..6092ec2d 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -143,7 +143,7 @@ public IQueryableAnalyzer QueryableAnalyzer public bool RenameParameterExpression { get; set; } = false; /// - /// Prevents any System.Linq.Expressions.ParameterExpression.Name from being empty. + /// Prevents any System.Linq.Expressions.ParameterExpression.Name value from being empty by substituting a random 16 character word. /// /// Default value is false. /// diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 8c0a3b38..f3e27d3e 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -1010,8 +1010,8 @@ public void DynamicExpressionParser_ParseLambda_RenameParameterExpression(string } [Theory] - [InlineData("c => c.Age == 8", "[a-z]{6} =\\> \\([a-z]{6}\\.Age == 8\\)")] - [InlineData("c => c.Name == \"test\"", "[a-z]{6} =\\> \\([a-z]{6}\\.Name == \"test\"\\)")] + [InlineData("c => c.Age == 8", "([a-z]{16}) =\\> \\(\\1\\.Age == 8\\)")] + [InlineData("c => c.Name == \"test\"", "([a-z]{16}) =\\> \\(\\1\\.Name == \"test\"\\)")] public void DynamicExpressionParser_ParseLambda_RenameEmptyParameterExpressionNames(string expressionAsString, string expected) { // Arrange diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs index fe86298c..afc6b89e 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ParameterExpressionHelperTests.cs @@ -59,22 +59,22 @@ public class ParameterExpressionHelperTests [InlineData(typeof(int), "it ge 1", @"\(Param_0 >= 1\)", "", false)] [InlineData(typeof(int), "it GreaterThanEqual 1", @"\(Param_0 >= 1\)", "", false)] - [InlineData(typeof(int), "it", "[a-z]{6}", "", true)] - [InlineData(typeof(int), "it == 1", @"\([a-z]{6} == 1\)", "", true)] - [InlineData(typeof(int), "it eq 1", @"\([a-z]{6} == 1\)", "", true)] - [InlineData(typeof(int), "it equal 1", @"\([a-z]{6} == 1\)", "", true)] - [InlineData(typeof(int), "it != 1", @"\([a-z]{6} != 1\)", "", true)] - [InlineData(typeof(int), "it ne 1", @"\([a-z]{6} != 1\)", "", true)] - [InlineData(typeof(int), "it neq 1", @"\([a-z]{6} != 1\)", "", true)] - [InlineData(typeof(int), "it notequal 1", @"\([a-z]{6} != 1\)", "", true)] - [InlineData(typeof(int), "it lt 1", @"\([a-z]{6} < 1\)", "", true)] - [InlineData(typeof(int), "it LessThan 1", @"\([a-z]{6} < 1\)", "", true)] - [InlineData(typeof(int), "it le 1", @"\([a-z]{6} <= 1\)", "", true)] - [InlineData(typeof(int), "it LessThanEqual 1", @"\([a-z]{6} <= 1\)", "", true)] - [InlineData(typeof(int), "it gt 1", @"\([a-z]{6} > 1\)", "", true)] - [InlineData(typeof(int), "it GreaterThan 1", @"\([a-z]{6} > 1\)", "", true)] - [InlineData(typeof(int), "it ge 1", @"\([a-z]{6} >= 1\)", "", true)] - [InlineData(typeof(int), "it GreaterThanEqual 1", @"\([a-z]{6} >= 1\)", "", true)] + [InlineData(typeof(int), "it", "[a-z]{16}", "", true)] + [InlineData(typeof(int), "it == 1", @"\([a-z]{16} == 1\)", "", true)] + [InlineData(typeof(int), "it eq 1", @"\([a-z]{16} == 1\)", "", true)] + [InlineData(typeof(int), "it equal 1", @"\([a-z]{16} == 1\)", "", true)] + [InlineData(typeof(int), "it != 1", @"\([a-z]{16} != 1\)", "", true)] + [InlineData(typeof(int), "it ne 1", @"\([a-z]{16} != 1\)", "", true)] + [InlineData(typeof(int), "it neq 1", @"\([a-z]{16} != 1\)", "", true)] + [InlineData(typeof(int), "it notequal 1", @"\([a-z]{16} != 1\)", "", true)] + [InlineData(typeof(int), "it lt 1", @"\([a-z]{16} < 1\)", "", true)] + [InlineData(typeof(int), "it LessThan 1", @"\([a-z]{16} < 1\)", "", true)] + [InlineData(typeof(int), "it le 1", @"\([a-z]{16} <= 1\)", "", true)] + [InlineData(typeof(int), "it LessThanEqual 1", @"\([a-z]{16} <= 1\)", "", true)] + [InlineData(typeof(int), "it gt 1", @"\([a-z]{16} > 1\)", "", true)] + [InlineData(typeof(int), "it GreaterThan 1", @"\([a-z]{16} > 1\)", "", true)] + [InlineData(typeof(int), "it ge 1", @"\([a-z]{16} >= 1\)", "", true)] + [InlineData(typeof(int), "it GreaterThanEqual 1", @"\([a-z]{16} >= 1\)", "", true)] [InlineData(typeof(bool), "it || true", @"\(x OrElse True\)", "x", false)] [InlineData(typeof(bool), "it or true", @"\(x OrElse True\)", "x", false)] @@ -85,9 +85,9 @@ public class ParameterExpressionHelperTests [InlineData(typeof(bool), "it || true", @"\(Param_0 OrElse True\)", "", false)] [InlineData(typeof(bool), "it or true", @"\(Param_0 OrElse True\)", "", false)] [InlineData(typeof(bool), "it OrElse true", @"\(Param_0 OrElse True\)", "", false)] - [InlineData(typeof(bool), "it || true", @"\([a-z]{6} OrElse True\)", "", true)] - [InlineData(typeof(bool), "it or true", @"\([a-z]{6} OrElse True\)", "", true)] - [InlineData(typeof(bool), "it OrElse true", @"\([a-z]{6} OrElse True\)", "", true)] + [InlineData(typeof(bool), "it || true", @"\([a-z]{16} OrElse True\)", "", true)] + [InlineData(typeof(bool), "it or true", @"\([a-z]{16} OrElse True\)", "", true)] + [InlineData(typeof(bool), "it OrElse true", @"\([a-z]{16} OrElse True\)", "", true)] [InlineData(typeof(bool), "it && true", @"\(x AndAlso True\)", "x", false)] [InlineData(typeof(bool), "it and true", @"\(x AndAlso True\)", "x", false)] @@ -98,9 +98,9 @@ public class ParameterExpressionHelperTests [InlineData(typeof(bool), "it && true", @"\(Param_0 AndAlso True\)", "", false)] [InlineData(typeof(bool), "it and true", @"\(Param_0 AndAlso True\)", "", false)] [InlineData(typeof(bool), "it AndAlso true", @"\(Param_0 AndAlso True\)", "", false)] - [InlineData(typeof(bool), "it && true", @"\([a-z]{6} AndAlso True\)", "", true)] - [InlineData(typeof(bool), "it and true", @"\([a-z]{6} AndAlso True\)", "", true)] - [InlineData(typeof(bool), "it AndAlso true", @"\([a-z]{6} AndAlso True\)", "", true)] + [InlineData(typeof(bool), "it && true", @"\([a-z]{16} AndAlso True\)", "", true)] + [InlineData(typeof(bool), "it and true", @"\([a-z]{16} AndAlso True\)", "", true)] + [InlineData(typeof(bool), "it AndAlso true", @"\([a-z]{16} AndAlso True\)", "", true)] public void ParameterExpressionHelper_CreateParameterExpression(Type type, string expression, string expectedResult, string substitute, bool renameEmpty) { // Arrange @@ -142,13 +142,13 @@ public void ParameterExpressionHelper_IsNullOrWhiteSpace(string input, bool expe } [Fact] - public void ParameterExpressionHelper_GenerateRandomWord() + public void ParameterExpressionHelper_GenerateRandom16CharWord() { // Arrange and Act string result = ParameterExpressionHelper.GenerateRandomWord(); // Assert - Check.That(result).Matches(@"[a-z]{6}"); + Check.That(result).Matches(@"[a-z]{16}"); } } } From b7949cebf466df5e3c1c8ad128108c94a02ba61e Mon Sep 17 00:00:00 2001 From: countincognito Date: Wed, 26 Feb 2020 22:49:48 +0000 Subject: [PATCH 6/6] Add comment to IsNullOrWhiteSpace method to explain why we cannot use string.IsNullOrWhiteSpace. --- .../ParameterExpressionHelper.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs b/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs index 377ed16d..0c42bd5c 100644 --- a/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs +++ b/src/System.Linq.Dynamic.Core/ParameterExpressionHelper.cs @@ -14,6 +14,20 @@ public static ParameterExpression CreateParameterExpression(Type type, string na return Expression.Parameter(type, paramName); } + /// + /// Indicates whether a specified string is null, empty, or consists only of white-space + /// characters. + /// + /// Recreates the same functionality as System.String.IsNullOrWhiteSpace but included here + /// for compatibility with net35. + /// + /// + /// The string to test. + /// + /// + /// true if the value parameter is null or System.String.Empty, or if value consists + /// exclusively of white-space characters. + /// internal static bool IsNullOrWhiteSpace(string value) { if (value == null)