-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-5375: Remove KnownSerializerRegistry. #1552
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
Conversation
| typeof(DateTimeExtensions), | ||
| typeof(LinqExtensions), | ||
| typeof(MongoEnumerable), | ||
| typeof(Mql), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep Mql.Constant(...) from being evaluated client-side.
| var funcParameters = funcLambda.Parameters; | ||
| var accumulatorParameter = funcParameters[0]; | ||
| var accumulatorSerializer = context.KnownSerializersRegistry.GetSerializer(accumulatorParameter); | ||
| var accumulatorSerializer = BsonSerializer.LookupSerializer(accumulatorParameter.Type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to have a factory for numeric return types to protect ourselves from users registering weird serializers for numeric types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the previous comment is moot. What we should be doing is using the serializer from the seedTranslation as the serializer for the accumulator.
| } | ||
| else | ||
| { | ||
| throw new ExpressionNotSupportedException(expression, because: "the registred serializer is not representation configurable"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, contains a typo... will fix in next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
| AggregationExpression ifTrueTranslation; | ||
| AggregationExpression ifFalseTranslation; | ||
| IBsonSerializer resultSerializer; | ||
| if (ifTrueExpression is ConstantExpression ifTrueConstantExpression) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If one result is a constant use the serializer of the other expression to serialize the constant.
|
|
||
| var ast = AstExpression.DateFromParts(yearTranslation.Ast, monthTranslation.Ast, dayTranslation.Ast, hourTranslation?.Ast, minuteTranslation?.Ast, secondTranslation?.Ast, millisecondTranslation?.Ast); | ||
| var serializer = context.KnownSerializersRegistry.GetSerializer(expression); | ||
| var serializer = DateTimeSerializer.Instance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to look up anything. We know which serializer we need.
| if (context.SymbolTable.TryGetSymbol(expression, out var symbol)) | ||
| { | ||
| var serializer = context.KnownSerializersRegistry.GetSerializer(expression); | ||
| var serializer = symbol.Serializer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to look up anything. We know which serializer we need.
|
|
||
| // public properties | ||
| public TranslationContextData Data => _data; | ||
| public KnownSerializersRegistry KnownSerializersRegistry => _knownSerializersRegistry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of making the serializer registry part of the context so we're not tied to using the global registry?
In the short term it's the same thing, but it looks towards the future where we want to specify different registries for different collections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea. Up to you whether you want to include it in this PR or create a separate one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to postpone doing anything like this. I don't even want to create a ticket for it until we know we need it.
By the way, I've been able to eliminate even more references to the serializer registry in the latest commit.
| (d => new R<E> { N = d.Id, V = d.I1 == E.E1 ? E.E1 : d.S2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : 'E1', else : '$S2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), | ||
| (d => new R<E> { N = d.Id, V = d.I1 == E.E1 ? d.S1 : d.S2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : '$S1', else : '$S2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), | ||
| (d => new R<E> { N = d.Id, V = d.S1 == E.E1 ? E.E1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : 'E1', else : 'E2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), | ||
| (d => new R<E> { N = d.Id, V = d.S1 == E.E1 ? Mql.Constant(E.E1, BsonType.String) : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : 'E1', else : 'E2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the scenario we were discussing in Slack.
Since the ternary operator has two constants we now default to using the registered serializer, but using Mql.Constant the user can override that.
JamesKovacs
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks great and allows us to remove a difficult-to-reason-about piece of code. One question around the TranslationContext and the now unused serializer parameter.
| } | ||
| var knownSerializersRegistry = KnownSerializerFinder.FindKnownSerializers(expression, serializer); | ||
| return new TranslationContext(symbolTable, nameGenerator, knownSerializersRegistry, translationOptions, data); | ||
| return new TranslationContext(symbolTable, nameGenerator, translationOptions, data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serializer is never used. Are we still translating $setWindowFields correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I've removed the unused serializer paramater.
$setWindowFields translators weren't using the KnownSerializerRegisty so they should continue to be translated correctly.
|
|
||
| // public properties | ||
| public TranslationContextData Data => _data; | ||
| public KnownSerializersRegistry KnownSerializersRegistry => _knownSerializersRegistry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea. Up to you whether you want to include it in this PR or create a separate one.
src/MongoDB.Driver/Mql.cs
Outdated
| /// <returns>The value.</returns> | ||
| public static TValue Constant<TValue>(TValue value, IBsonSerializer<TValue> serializer) | ||
| { | ||
| throw new NotSupportedException("This method is not functional. It is only usable in MongoDB LINQ queries."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't we moved this to helper method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes... in a different PR that was not yet pushed to main when this code was written.
I will change this now that that PR has been pushed to main.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to do this I'm rebasing the PR on main.
rstam
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed all comments.
Probably needs at least one more careful pass before we decide it's ready to merge (for example the TODO comment).
| var funcParameters = funcLambda.Parameters; | ||
| var accumulatorParameter = funcParameters[0]; | ||
| var accumulatorSerializer = context.KnownSerializersRegistry.GetSerializer(accumulatorParameter); | ||
| var accumulatorSerializer = BsonSerializer.LookupSerializer(accumulatorParameter.Type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the previous comment is moot. What we should be doing is using the serializer from the seedTranslation as the serializer for the accumulator.
| } | ||
| else | ||
| { | ||
| throw new ExpressionNotSupportedException(expression, because: "the registred serializer is not representation configurable"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
|
||
| // public properties | ||
| public TranslationContextData Data => _data; | ||
| public KnownSerializersRegistry KnownSerializersRegistry => _knownSerializersRegistry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to postpone doing anything like this. I don't even want to create a ticket for it until we know we need it.
By the way, I've been able to eliminate even more references to the serializer registry in the latest commit.
src/MongoDB.Driver/Mql.cs
Outdated
| /// <returns>The value.</returns> | ||
| public static TValue Constant<TValue>(TValue value, IBsonSerializer<TValue> serializer) | ||
| { | ||
| throw new NotSupportedException("This method is not functional. It is only usable in MongoDB LINQ queries."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to do this I'm rebasing the PR on main.
| { | ||
| var partiallyEvaluatedOutput = (Expression<Func<TGrouping, TOutput>>)PartialEvaluator.EvaluatePartially(_output); | ||
| var context = TranslationContext.Create(partiallyEvaluatedOutput, inputSerializer, translationOptions); | ||
| var context = TranslationContext.Create(partiallyEvaluatedOutput, translationOptions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create method no longer has a serializer parameter.
| Type { IsConstructedGenericType: true } t when t.GetGenericTypeDefinition() == typeof(Nullable<>) => (IBsonSerializer)Activator.CreateInstance(typeof(NullableSerializer<>).MakeGenericType(t.GenericTypeArguments[0])), | ||
| Type { IsArray: true } t => (IBsonSerializer)Activator.CreateInstance(typeof(ArraySerializer<>).MakeGenericType(t.GetElementType())), | ||
| _ => context.KnownSerializersRegistry.GetSerializer(expression) // Required for Coalesce | ||
| _ => BsonSerializer.LookupSerializer(expression.Type) // Required for Coalesce |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: look into this a bit more and confirm whether a lookup is justified.
| var funcParameters = funcLambda.Parameters; | ||
| var accumulatorParameter = funcParameters[0]; | ||
| var accumulatorSerializer = context.KnownSerializersRegistry.GetSerializer(accumulatorParameter); | ||
| var accumulatorSerializer = seedTranslation.Serializer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No lookup is needed. We know which serializer to use.
| var: AstExpression.VarBinding(resultSelectorAccumulatorSymbol.Var, ast), | ||
| @in: resultSelectorTranslation.Ast); | ||
| serializer = context.KnownSerializersRegistry.GetSerializer(resultSelectorLambda); | ||
| serializer = resultSelectorTranslation.Serializer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No lookup is needed. We know which serializer to use.
| var resultSelectorSymbol = context.CreateSymbol(resultSelectorParameter, resultSelectorParameterSerializer); | ||
| var resultSelectorContext = context.WithSymbol(resultSelectorSymbol); | ||
| var resultSelectorAccumulatorParameter = resultSelectorLambda.Parameters[0]; | ||
| var resultSelectorAccumulatorSymbol = context.CreateSymbol(resultSelectorAccumulatorParameter, accumulatorSerializer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No lookup is needed. We know which serializer to use.
| }; | ||
|
|
||
| var actualTypeSerializer = context.KnownSerializersRegistry.GetSerializer(sourceExpression); | ||
| var actualTypeSerializer = BsonSerializer.LookupSerializer(actualType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future we might add some way to ask a serializer which serializer to use for a derived type.
For now a lookup is the only option available.
sanych-sun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
sanych-sun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM + minor suggestion
|
|
||
| if (type.IsNullable(out var valueType) && TryGetSerializer(valueType, out var valueSerializer)) | ||
| { | ||
| serializer = NullableSerializer.Create(valueSerializer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it makes sense to use singletons for Nullable<> in the same way as we have for other standard serializers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a number of changes to this class based on discussions in Slack.
| __standardSerializers.Add(typeof(uint), new UInt32Serializer(representation: BsonType.Int32, strictConverter)); | ||
| __standardSerializers.Add(typeof(ulong), new UInt64Serializer(representation: BsonType.Int64, strictConverter)); | ||
|
|
||
| var standardTypes = __standardSerializers.Keys.ToArray(); // call ToArray to make a copy of the current Keys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we implement local method RegisterSerializer instead, like:
void RegisterSerializer(IBsonSerializer serializer)
{
__standardSerializers.Add(serializer.ValueType, serializer);
var nullableSerializer = NullableSerializer.Create(serializer);
__standardSerializers.Add(nullableSerializer.ValueType, nullableSerializer);
var arraySerializer = ArraySerializerHelper.CreateSerializer(serializer);
__standardSerializers.Add(arraySerializer.ValueType, nullableSerializer);
var arrayOfNullableSerializer = ArraySerializerHelper.CreateSerializer(itemSerializer: nullableSerializer);
__standardSerializers.Add(arrayOfNullableSerializer.ValueType, arrayOfNullableSerializer);
}
As far as I understood serializer.ValueType - this is the exact type that serializer will process, so we do not have to construct it here, but can relay on what serializer says.
And then simply call it for each serializer, like:
RegisterSerializer(new BooleanSerializer(representation: BsonType.Boolean));
RegisterSerializer(new ByteSerializer(representation: BsonType.Int32));
RegisterSerializer(new CharSerializer(representation: BsonType.Int32));
RegisterSerializer(new DecimalSerializer(representation: BsonType.Decimal128, allowTruncationConverter));
RegisterSerializer(new DoubleSerializer(representation: BsonType.Double, strictConverter));
RegisterSerializer(new Int16Serializer(representation: BsonType.Int32, strictConverter));
RegisterSerializer(new Int32Serializer(representation: BsonType.Int32, strictConverter));
RegisterSerializer(new Int64Serializer(representation: BsonType.Int64, strictConverter));
RegisterSerializer(new SByteSerializer(representation: BsonType.Int32));
RegisterSerializer(new SingleSerializer(representation: BsonType.Double, allowTruncationConverter));
RegisterSerializer(new UInt16Serializer(representation: BsonType.Int32, strictConverter));
RegisterSerializer(new UInt32Serializer(representation: BsonType.Int32, strictConverter));
RegisterSerializer(new UInt64Serializer(representation: BsonType.Int64, strictConverter));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it! Done.
Only difference is I named the local method AddStandardSerializers instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please check the failed tests.
JamesKovacs
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
sanych-sun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No description provided.