Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested Cosmos Db compatibility #352

Merged
merged 6 commits into from
Mar 18, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions System.Linq.Dynamic.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RootNamespace>ConsoleAppCosmosDb</RootNamespace>
<AssemblyName>ConsoleAppCosmosDb</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.9.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core\System.Linq.Dynamic.Core.csproj" />
</ItemGroup>

</Project>
11 changes: 11 additions & 0 deletions src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace ConsoleAppCosmosDb.Entities
{
[Serializable]
public class Entity
{
public Guid Id { get; set; }
public EntityData Data { get; set; }
}
}
11 changes: 11 additions & 0 deletions src-console/ConsoleApp_netcore2.1_CosmosDb/Entities/EntityData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;

namespace ConsoleAppCosmosDb.Entities
{
[Serializable]
public class EntityData
{
public IList<EntityReading> Readings { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace ConsoleAppCosmosDb.Entities
{
[Serializable]
public class EntityReadingValue
{
public int Weight { get; set; }
}
}
253 changes: 253 additions & 0 deletions src-console/ConsoleApp_netcore2.1_CosmosDb/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
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<Database> GetOrCreateDatabaseAsync(string databaseId)
{
return await _Client.CreateDatabaseIfNotExistsAsync(new Database { Id = databaseId });
}

private static async Task<DocumentCollection> 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<Entity>()
{
new Entity
{
Id = Guid.NewGuid(),
Data = new EntityData
{
Readings = new List<EntityReading>
{
new EntityReading
{
Status = "valid",
Weight = 10,
},
new EntityReading
{
Status = "invalid",
Weight = 2,
},
},
},
},
new Entity
{
Id = Guid.NewGuid(),
Data = new EntityData
{
Readings = new List<EntityReading>
{
new EntityReading
{
Status = "valid",
Weight = 0,
Value = new EntityReadingValue
{
Weight = 0,
},
},
},
},
},
new Entity
{
Id = Guid.NewGuid(),
Data = new EntityData
{
Readings = new List<EntityReading>
{
new EntityReading
{
Status = "Valid",
Weight = 12,
},
},
},
},
new Entity
{
Id = Guid.NewGuid(),
Data = new EntityData
{
Readings = new List<EntityReading>
{
new EntityReading
{
Status = "VALID",
Weight = 9,
Value = new EntityReadingValue
{
Weight = 0,
},
},
},
},
},
new Entity
{
Id = Guid.NewGuid(),
Data = new EntityData
{
Readings = new List<EntityReading>
{
new EntityReading
{
Status = "invalid",
Weight = 5,
},
},
},
},
new Entity
{
Id = Guid.NewGuid(),
Data = new EntityData
{
Readings = new List<EntityReading>
{
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<Entity> entities = _Client.CreateDocumentQuery<Entity>(collectionUri);

IQueryable<Entity> 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<Entity>(collectionUri, querySqlSpec, new FeedOptions() { EnableCrossPartitionQuery = true });
IList<Entity> 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));
}
}
}
4 changes: 2 additions & 2 deletions src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public static Expression<Func<T, TResult>> ParseLambda<T, TResult>([CanBeNull] P
{
Check.NotEmpty(expression, nameof(expression));

return (Expression<Func<T, TResult>>)ParseLambda(parsingConfig, createParameterCtor, new[] { ParameterExpressionHelper.CreateParameterExpression(typeof(T), string.Empty) }, typeof(TResult), expression, values);
return (Expression<Func<T, TResult>>)ParseLambda(parsingConfig, createParameterCtor, new[] { ParameterExpressionHelper.CreateParameterExpression(typeof(T), string.Empty, parsingConfig?.RenameEmptyParameterExpressionNames ?? false) }, typeof(TResult), expression, values);
}

/// <summary>
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand Down
Loading