-
Notifications
You must be signed in to change notification settings - Fork 523
[Internal] Query: Adds interface for linq serialization functions #4163
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
Merged
microsoft-github-policy-service
merged 16 commits into
master
from
users/mayapainter/linqtranslationupdate
Nov 8, 2023
Merged
Changes from 15 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
f17a85b
intial commit
Maya-Painter 9a1f18c
add interface
Maya-Painter 085734f
PR comments and TranslationContext cleanup
Maya-Painter ff1c077
update params
Maya-Painter 9e2ba63
fix parameters
Maya-Painter 2d13dfc
PR comments
Maya-Painter 0e15171
PR comments
Maya-Painter 1a5692c
PR comments
Maya-Painter ab89f81
simplifying serializer class
Maya-Painter 7775041
Merge branch 'master' into users/mayapainter/linqtranslationupdate
Maya-Painter c887719
interface updates
Maya-Painter a7d81cf
Update docs
Maya-Painter 1c91cdc
Merge branch 'master' into users/mayapainter/linqtranslationupdate
Maya-Painter 090d949
PR comments
Maya-Painter ad0881a
PR comments
Maya-Painter 5dac219
PR comments - rename and fix assert
Maya-Painter File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
Microsoft.Azure.Cosmos/src/Linq/CosmosElementToSqlScalarExpressionVisitor.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| //------------------------------------------------------------ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| //------------------------------------------------------------ | ||
| namespace Microsoft.Azure.Cosmos.Linq | ||
| { | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Collections.Immutable; | ||
| using System.Diagnostics; | ||
| using Microsoft.Azure.Cosmos.CosmosElements; | ||
| using Microsoft.Azure.Cosmos.CosmosElements.Numbers; | ||
| using Microsoft.Azure.Cosmos.SqlObjects; | ||
|
|
||
| internal sealed class CosmosElementToSqlScalarExpressionVisitor : ICosmosElementVisitor<SqlScalarExpression> | ||
| { | ||
| public static readonly CosmosElementToSqlScalarExpressionVisitor Singleton = new CosmosElementToSqlScalarExpressionVisitor(); | ||
|
|
||
| private CosmosElementToSqlScalarExpressionVisitor() | ||
| { | ||
| // Private constructor, since this class is a singleton. | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosArray cosmosArray) | ||
| { | ||
| List<SqlScalarExpression> items = new List<SqlScalarExpression>(); | ||
| foreach (CosmosElement item in cosmosArray) | ||
| { | ||
| items.Add(item.Accept(this)); | ||
| } | ||
|
|
||
| return SqlArrayCreateScalarExpression.Create(items.ToImmutableArray()); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosBinary cosmosBinary) | ||
| { | ||
| // Can not convert binary to scalar expression without knowing the API type. | ||
| Debug.Fail("CosmosElementToSqlScalarExpressionVisitor Assert", "Unreachable"); | ||
| throw new InvalidOperationException(); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosBoolean cosmosBoolean) | ||
| { | ||
| return SqlLiteralScalarExpression.Create(SqlBooleanLiteral.Create(cosmosBoolean.Value)); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosGuid cosmosGuid) | ||
| { | ||
| // Can not convert guid to scalar expression without knowing the API type. | ||
| Debug.Fail("CosmosElementToSqlScalarExpressionVisitor Assert", "Unreachable"); | ||
| throw new InvalidOperationException(); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosNull cosmosNull) | ||
| { | ||
| return SqlLiteralScalarExpression.Create(SqlNullLiteral.Create()); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosNumber cosmosNumber) | ||
| { | ||
| if (!(cosmosNumber is CosmosNumber64 cosmosNumber64)) | ||
| { | ||
| throw new ArgumentException($"Unknown {nameof(CosmosNumber)} type: {cosmosNumber.GetType()}."); | ||
| } | ||
|
|
||
| return SqlLiteralScalarExpression.Create(SqlNumberLiteral.Create(cosmosNumber64.GetValue())); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosObject cosmosObject) | ||
| { | ||
| List<SqlObjectProperty> properties = new List<SqlObjectProperty>(); | ||
| foreach (KeyValuePair<string, CosmosElement> prop in cosmosObject) | ||
| { | ||
| SqlPropertyName name = SqlPropertyName.Create(prop.Key); | ||
| CosmosElement value = prop.Value; | ||
| SqlScalarExpression expression = value.Accept(this); | ||
| SqlObjectProperty property = SqlObjectProperty.Create(name, expression); | ||
| properties.Add(property); | ||
| } | ||
|
|
||
| return SqlObjectCreateScalarExpression.Create(properties.ToImmutableArray()); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosString cosmosString) | ||
| { | ||
| return SqlLiteralScalarExpression.Create(SqlStringLiteral.Create(cosmosString.Value)); | ||
| } | ||
|
|
||
| public SqlScalarExpression Visit(CosmosUndefined cosmosUndefined) | ||
| { | ||
| return SqlLiteralScalarExpression.Create(SqlUndefinedLiteral.Create()); | ||
| } | ||
| } | ||
| } |
110 changes: 110 additions & 0 deletions
110
Microsoft.Azure.Cosmos/src/Linq/DefaultCosmosLinqSerializer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| //------------------------------------------------------------ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| //------------------------------------------------------------ | ||
| namespace Microsoft.Azure.Cosmos.Linq | ||
| { | ||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Linq.Expressions; | ||
| using System.Reflection; | ||
| using System.Runtime.Serialization; | ||
| using Microsoft.Azure.Documents; | ||
| using Newtonsoft.Json; | ||
|
|
||
| internal class DefaultCosmosLinqSerializer : ICosmosLinqSerializer | ||
| { | ||
| public bool RequiresCustomSerialization(MemberExpression memberExpression, Type memberType) | ||
| { | ||
| // There are two ways to specify a custom attribute | ||
| // 1- by specifying the JsonConverterAttribute on a Class/Enum | ||
| // [JsonConverter(typeof(StringEnumConverter))] | ||
| // Enum MyEnum | ||
| // { | ||
| // ... | ||
| // } | ||
| // | ||
| // 2- by specifying the JsonConverterAttribute on a property | ||
| // class MyClass | ||
| // { | ||
| // [JsonConverter(typeof(StringEnumConverter))] | ||
| // public MyEnum MyEnum; | ||
| // } | ||
| // | ||
| // Newtonsoft gives high precedence to the attribute specified | ||
| // on a property over on a type (class/enum) | ||
| // so we check both attributes and apply the same precedence rules | ||
| // JsonConverterAttribute doesn't allow duplicates so it's safe to | ||
| // use FirstOrDefault() | ||
| CustomAttributeData memberAttribute = memberExpression.Member.CustomAttributes.FirstOrDefault(ca => ca.AttributeType == typeof(Newtonsoft.Json.JsonConverterAttribute)); | ||
| CustomAttributeData typeAttribute = memberType.GetsCustomAttributes().FirstOrDefault(ca => ca.AttributeType == typeof(Newtonsoft.Json.JsonConverterAttribute)); | ||
|
|
||
| return memberAttribute != null || typeAttribute != null; | ||
| } | ||
|
|
||
| public string Serialize(object value, MemberExpression memberExpression, Type memberType) | ||
| { | ||
| CustomAttributeData memberAttribute = memberExpression.Member.CustomAttributes.FirstOrDefault(ca => ca.AttributeType == typeof(Newtonsoft.Json.JsonConverterAttribute)); | ||
| CustomAttributeData typeAttribute = memberType.GetsCustomAttributes().FirstOrDefault(ca => ca.AttributeType == typeof(Newtonsoft.Json.JsonConverterAttribute)); | ||
| CustomAttributeData converterAttribute = memberAttribute ?? typeAttribute; | ||
|
|
||
| Debug.Assert(converterAttribute.ConstructorArguments.Count > 0); | ||
| Type converterType = (Type)converterAttribute.ConstructorArguments[0].Value; | ||
|
|
||
| string serializedValue = converterType.GetConstructor(Type.EmptyTypes) != null | ||
| ? JsonConvert.SerializeObject(value, (Newtonsoft.Json.JsonConverter)Activator.CreateInstance(converterType)) | ||
| : JsonConvert.SerializeObject(value); | ||
|
|
||
| return serializedValue; | ||
| } | ||
|
|
||
| public string SerializeScalarExpression(ConstantExpression inputExpression) | ||
| { | ||
| return JsonConvert.SerializeObject(inputExpression.Value); | ||
| } | ||
|
|
||
| public string GetMemberName(MemberInfo memberInfo, CosmosLinqSerializerOptions linqSerializerOptions = null) | ||
| { | ||
| string memberName = null; | ||
|
|
||
| // Check if Newtonsoft JsonExtensionDataAttribute is present on the member, if so, return empty member name. | ||
| Newtonsoft.Json.JsonExtensionDataAttribute jsonExtensionDataAttribute = memberInfo.GetCustomAttribute<Newtonsoft.Json.JsonExtensionDataAttribute>(true); | ||
| if (jsonExtensionDataAttribute != null && jsonExtensionDataAttribute.ReadData) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| // Json.Net honors JsonPropertyAttribute more than DataMemberAttribute | ||
| // So we check for JsonPropertyAttribute first. | ||
| JsonPropertyAttribute jsonPropertyAttribute = memberInfo.GetCustomAttribute<JsonPropertyAttribute>(true); | ||
| if (jsonPropertyAttribute != null && !string.IsNullOrEmpty(jsonPropertyAttribute.PropertyName)) | ||
| { | ||
| memberName = jsonPropertyAttribute.PropertyName; | ||
| } | ||
| else | ||
| { | ||
| DataContractAttribute dataContractAttribute = memberInfo.DeclaringType.GetCustomAttribute<DataContractAttribute>(true); | ||
| if (dataContractAttribute != null) | ||
| { | ||
| DataMemberAttribute dataMemberAttribute = memberInfo.GetCustomAttribute<DataMemberAttribute>(true); | ||
| if (dataMemberAttribute != null && !string.IsNullOrEmpty(dataMemberAttribute.Name)) | ||
| { | ||
| memberName = dataMemberAttribute.Name; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (memberName == null) | ||
| { | ||
| memberName = memberInfo.Name; | ||
| } | ||
|
|
||
| if (linqSerializerOptions != null) | ||
| { | ||
| memberName = CosmosSerializationUtil.GetStringWithPropertyNamingPolicy(linqSerializerOptions, memberName); | ||
| } | ||
|
|
||
| return memberName; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.