Skip to content

Commit

Permalink
Part of JSON primitive collection serialization
Browse files Browse the repository at this point in the history
Part of #30677

There is still work to do here, but this is basically working so I wanted to get it out for review.

Remaining work:
- Negative cases/exception messages
- Re-introduce checks for whether or not query supports primitive collections
- Cases where the primitive collection object exists and should be added to
- Custom element type mapping on the property
- Custom element reader/writer
- Custom collection reader/writer
- Use default element type mapping configured in ConfigureConventions
- Update Cosmos primitive collection mapping
- More query tests!
  • Loading branch information
ajcvickers committed Jul 19, 2023
1 parent 614dfc3 commit 6b44512
Show file tree
Hide file tree
Showing 77 changed files with 3,359 additions and 1,064 deletions.
7 changes: 5 additions & 2 deletions src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ protected CosmosTypeMapping(CoreTypeMappingParameters parameters)
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override CoreTypeMapping Clone(ValueConverter? converter)
=> new CosmosTypeMapping(Parameters.WithComposedConverter(converter));
public override CoreTypeMapping Clone(
ValueConverter? converter,
CoreTypeMapping? elementMapping = null,
JsonValueReaderWriter? jsonValueReaderWriter = null)
=> new CosmosTypeMapping(Parameters.WithComposedConverter(converter, elementMapping, jsonValueReaderWriter));
}
7 changes: 5 additions & 2 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ private InMemoryTypeMapping(CoreTypeMappingParameters parameters)
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override CoreTypeMapping Clone(ValueConverter? converter)
=> new InMemoryTypeMapping(Parameters.WithComposedConverter(converter));
public override CoreTypeMapping Clone(
ValueConverter? converter,
CoreTypeMapping? elementMapping = null,
JsonValueReaderWriter? jsonValueReaderWriter = null)
=> new InMemoryTypeMapping(Parameters.WithComposedConverter(converter, elementMapping, jsonValueReaderWriter));
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,18 @@ when entityQueryRootExpression.GetType() == typeof(EntityQueryRootExpression)

case SqlQueryRootExpression sqlQueryRootExpression:
{
var typeMapping = RelationalDependencies.TypeMappingSource.FindMapping(sqlQueryRootExpression.ElementType);
var typeMapping = RelationalDependencies.TypeMappingSource.FindMapping(
sqlQueryRootExpression.ElementType, RelationalDependencies.Model);

if (typeMapping == null)
{
throw new InvalidOperationException(
RelationalStrings.SqlQueryUnmappedType(sqlQueryRootExpression.ElementType.DisplayName()));
}

var selectExpression = new SelectExpression(
new FromSqlExpression("t", sqlQueryRootExpression.Sql, sqlQueryRootExpression.Argument), SqlQuerySingleColumnAlias, sqlQueryRootExpression.Type, typeMapping);
new FromSqlExpression("t", sqlQueryRootExpression.Sql, sqlQueryRootExpression.Argument), SqlQuerySingleColumnAlias,
sqlQueryRootExpression.Type, typeMapping);

Expression shaperExpression = new ProjectionBindingExpression(
selectExpression, new ProjectionMember(), sqlQueryRootExpression.ElementType.MakeNullable());
Expand Down Expand Up @@ -334,7 +337,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp

var rowExpressions = new List<RowValueExpression>();
var encounteredNull = false;
var intTypeMapping = _typeMappingSource.FindMapping(typeof(int));
var intTypeMapping = _typeMappingSource.FindMapping(typeof(int), RelationalDependencies.Model);

for (var i = 0; i < inlineQueryRootExpression.Values.Count; i++)
{
Expand All @@ -353,13 +356,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
encounteredNull |=
translatedValue is not SqlConstantExpression { Value: not null } and not ColumnExpression { IsNullable: false };

rowExpressions.Add(new RowValueExpression(new[]
{
// Since VALUES may not guarantee row ordering, we add an _ord value by which we'll order.
_sqlExpressionFactory.Constant(i, intTypeMapping),
// Note that for the actual value, we must leave the type mapping null to allow it to get inferred later based on usage
translatedValue
}));
rowExpressions.Add(
new RowValueExpression(
new[]
{
// Since VALUES may not guarantee row ordering, we add an _ord value by which we'll order.
_sqlExpressionFactory.Constant(i, intTypeMapping),
// Note that for the actual value, we must leave the type mapping null to allow it to get inferred later based on usage
translatedValue
}));
}

if (rowExpressions.Count == 0)
Expand Down Expand Up @@ -693,7 +698,9 @@ protected override ShapedQueryExpression TranslateExcept(ShapedQueryExpression s
{
// This could be group by entity type
if (remappedKeySelector is not EntityShaperExpression
{ ValueBufferExpression: ProjectionBindingExpression pbe } ese)
{
ValueBufferExpression: ProjectionBindingExpression pbe
} ese)
{
// ValueBufferExpression can be JsonQuery, ProjectionBindingExpression, EntityProjection
// We only allow ProjectionBindingExpression which represents a regular entity
Expand Down Expand Up @@ -1319,7 +1326,7 @@ static bool AreOtherNonOwnedEntityTypesInTheTable(IEntityType rootType, ITableBa
{
var typeBase = entityTypeMapping.TypeBase;
if ((entityTypeMapping.IsSharedTablePrincipal == true
&& typeBase != rootType)
&& typeBase != rootType)
|| (entityTypeMapping.IsSharedTablePrincipal == false
&& typeBase is IEntityType entityType
&& entityType.GetRootType() != rootType
Expand Down Expand Up @@ -1838,19 +1845,20 @@ protected virtual bool IsValidSelectExpressionForExecuteUpdate(
/// </summary>
/// <param name="expression">The query expression to process.</param>
/// <param name="inferredTypeMappings">
/// Inferred type mappings for queryable constants/parameters collected during translation. These will be applied to the appropriate
/// nodes in the tree.
/// Inferred type mappings for queryable constants/parameters collected during translation. These will be applied to the appropriate
/// nodes in the tree.
/// </param>
protected virtual Expression ApplyInferredTypeMappings(
Expression expression,
IReadOnlyDictionary<(TableExpressionBase, string), RelationalTypeMapping?> inferredTypeMappings)
=> new RelationalInferredTypeMappingApplier(_sqlExpressionFactory, inferredTypeMappings).Visit(expression);
=> new RelationalInferredTypeMappingApplier(
RelationalDependencies.Model, _sqlExpressionFactory, inferredTypeMappings).Visit(expression);

/// <summary>
/// Determines whether the given <see cref="SelectExpression" /> is ordered, typically because orderings have been added to it.
/// </summary>
/// <param name="selectExpression">The <see cref="SelectExpression" /> to check for ordering.</param>
/// <returns>Whether <paramref name="selectExpression"/> is ordered.</returns>
/// <returns>Whether <paramref name="selectExpression" /> is ordered.</returns>
protected virtual bool IsOrdered(SelectExpression selectExpression)
=> selectExpression.Orderings.Count > 0;

Expand Down Expand Up @@ -2753,16 +2761,24 @@ protected class RelationalInferredTypeMappingApplier : ExpressionVisitor
/// <summary>
/// Creates a new instance of the <see cref="RelationalInferredTypeMappingApplier" /> class.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="sqlExpressionFactory">The SQL expression factory.</param>
/// <param name="inferredTypeMappings">The inferred type mappings to be applied back on their query roots.</param>
public RelationalInferredTypeMappingApplier(
IModel model,
ISqlExpressionFactory sqlExpressionFactory,
IReadOnlyDictionary<(TableExpressionBase, string), RelationalTypeMapping?> inferredTypeMappings)
{
Model = model;
_sqlExpressionFactory = sqlExpressionFactory;
_inferredTypeMappings = inferredTypeMappings;
}

/// <summary>
/// The model.
/// </summary>
protected virtual IModel Model { get; }

/// <summary>
/// Attempts to find an inferred type mapping for the given table column.
/// </summary>
Expand Down Expand Up @@ -2883,7 +2899,7 @@ protected virtual ValuesExpression ApplyTypeMappingsOnValuesExpression(ValuesExp
newRowValues[i] = new RowValueExpression(newValues);
}

return new(valuesExpression.Alias, newRowValues, newColumnNames, valuesExpression.GetAnnotations());
return new ValuesExpression(valuesExpression.Alias, newRowValues, newColumnNames, valuesExpression.GetAnnotations());
}
}
}
Loading

0 comments on commit 6b44512

Please sign in to comment.