diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs index 35641457b6c..f33c85fd726 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs @@ -371,8 +371,13 @@ protected override Expression VisitExtension(Expression extensionExpression) _projectionMapping[_projectionMembers.Peek()] = projection; - return shaper.Update( - new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), typeof(ValueBuffer))); + return shaper + .Update(new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), typeof(ValueBuffer))) +#pragma warning disable EF1001 + // This is to handle have correct type for the shaper expression. It is later fixed in MatchTypes. + // This mirrors for structural types what we do for scalars. + .MakeClrTypeNullable(); +#pragma warning restore EF1001 } case IncludeExpression includeExpression: @@ -658,7 +663,14 @@ private static Expression MatchTypes(Expression expression, Type targetType) targetType.MakeNullable() == expression.Type, $"expression has type {expression.Type.Name}, but must be nullable over {targetType.Name}"); - expression = Expression.Convert(expression, targetType); + return expression switch + { +#pragma warning disable EF1001 + RelationalStructuralTypeShaperExpression structuralShaper => structuralShaper.MakeClrTypeNonNullable(), +#pragma warning restore EF1001 + + _ => Expression.Convert(expression, targetType), + }; } return expression; diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.StructuralEquality.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.StructuralEquality.cs index 3429b624107..9d8f54903b9 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.StructuralEquality.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.StructuralEquality.cs @@ -333,12 +333,12 @@ bool TryRewriteComplexTypeEquality(bool collection, [NotNullWhen(true)] out SqlE // into complex properties to generate a flattened list of comparisons. // The moment we reach a a complex property that's mapped to JSON, we stop and generate a single comparison // for the whole complex type. - bool TryGenerateComparisons( - IComplexType type, - Expression left, - Expression right, - [NotNullWhen(true)] ref SqlExpression? comparisons) + bool TryGenerateComparisons(IComplexType complexType, Expression left, Expression right, [NotNullWhen(true)] ref SqlExpression? comparisons) + => TryGenerateComparisonsRec(complexType, left, right, ref comparisons, out _); + bool TryGenerateComparisonsRec(IComplexType type, Expression left, Expression right, [NotNullWhen(true)] ref SqlExpression? comparisons, out bool exitImmediately) { + exitImmediately = false; + if (type.IsMappedToJson()) { var leftScalar = Process(left); @@ -418,6 +418,17 @@ SqlParameterExpression parameter { var comparison = _sqlExpressionFactory.MakeBinary(nodeType, leftTranslation, rightTranslation, boolTypeMapping)!; + // If we have a required property and one of the sides is a constant null, + // we can use just that property and skip comparing the rest of properties. + if (!property.IsNullable + && (leftTranslation is SqlConstantExpression { Value: null } + || rightTranslation is SqlConstantExpression { Value: null })) + { + comparisons = comparison; + exitImmediately = true; + return true; + } + comparisons = comparisons is null ? comparison : nodeType == ExpressionType.Equal @@ -447,10 +458,15 @@ SqlParameterExpression parameter if (nestedLeft is null || nestedRight is null - || !TryGenerateComparisons(complexProperty.ComplexType, nestedLeft, nestedRight, ref comparisons)) + || !TryGenerateComparisonsRec(complexProperty.ComplexType, nestedLeft, nestedRight, ref comparisons, out exitImmediately)) { return false; } + + if (exitImmediately) + { + return true; + } } return comparisons is not null; diff --git a/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs b/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs index 8fd0aede428..6f8d765dc9b 100644 --- a/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs +++ b/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs @@ -24,7 +24,7 @@ public class RelationalStructuralTypeShaperExpression : StructuralTypeShaperExpr /// An expression of ValueBuffer to get values for properties of the entity. /// A bool value indicating whether this entity instance can be null. public RelationalStructuralTypeShaperExpression(ITypeBase structuralType, Expression valueBufferExpression, bool nullable) - : base(structuralType, valueBufferExpression, nullable, materializationCondition: null) + : base(structuralType, valueBufferExpression, nullable) { } @@ -38,12 +38,14 @@ public RelationalStructuralTypeShaperExpression(ITypeBase structuralType, Expres /// An expression of to determine which entity type to /// materialize. /// + /// CLR type for this expression as returned from . protected RelationalStructuralTypeShaperExpression( ITypeBase type, Expression valueBufferExpression, bool nullable, - LambdaExpression? materializationCondition) - : base(type, valueBufferExpression, nullable, materializationCondition) + LambdaExpression? materializationCondition, + Type clrType) + : base(type, valueBufferExpression, nullable, materializationCondition, clrType) { } @@ -149,7 +151,7 @@ protected override LambdaExpression GenerateMaterializationCondition(ITypeBase t /// public override StructuralTypeShaperExpression WithType(ITypeBase type) => type != StructuralType - ? new RelationalStructuralTypeShaperExpression(type, ValueBufferExpression, IsNullable) + ? new RelationalStructuralTypeShaperExpression(type, ValueBufferExpression, IsNullable, materializationCondition: null, type.ClrType) : this; /// @@ -175,12 +177,24 @@ public override StructuralTypeShaperExpression MakeNullable(bool nullable = true } // Marking nullable requires re-computation of Discriminator condition - return new RelationalStructuralTypeShaperExpression(StructuralType, newValueBufferExpression, true); + return new RelationalStructuralTypeShaperExpression(StructuralType, newValueBufferExpression, true, materializationCondition: null, Type); } /// public override StructuralTypeShaperExpression Update(Expression valueBufferExpression) => valueBufferExpression != ValueBufferExpression - ? new RelationalStructuralTypeShaperExpression(StructuralType, valueBufferExpression, IsNullable, MaterializationCondition) + ? new RelationalStructuralTypeShaperExpression(StructuralType, valueBufferExpression, IsNullable, MaterializationCondition, Type) + : this; + + /// + public override StructuralTypeShaperExpression MakeClrTypeNullable() + => Type != Type.MakeNullable() + ? new RelationalStructuralTypeShaperExpression(StructuralType, ValueBufferExpression, IsNullable, MaterializationCondition, Type.MakeNullable()) + : this; + + /// + public override StructuralTypeShaperExpression MakeClrTypeNonNullable() + => Type != Type.UnwrapNullableType() + ? new RelationalStructuralTypeShaperExpression(StructuralType, ValueBufferExpression, IsNullable, MaterializationCondition, Type.UnwrapNullableType()) : this; } diff --git a/src/EFCore/Query/EntityMaterializerSourceParameters.cs b/src/EFCore/Query/EntityMaterializerSourceParameters.cs index 659e5606bbc..95750d7da96 100644 --- a/src/EFCore/Query/EntityMaterializerSourceParameters.cs +++ b/src/EFCore/Query/EntityMaterializerSourceParameters.cs @@ -8,14 +8,14 @@ namespace Microsoft.EntityFrameworkCore.Query; /// /// The entity or complex type being materialized. /// The name of the instance being materialized. -/// Whether nullable result is allowed. +/// CLR type of the result. /// /// The query tracking behavior, or if this materialization is not from a query. /// public readonly record struct StructuralTypeMaterializerSourceParameters( ITypeBase StructuralType, string InstanceName, - bool? AllowNullable, + Type ClrType, QueryTrackingBehavior? QueryTrackingBehavior); /// diff --git a/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs index 040ac24b656..3154069780a 100644 --- a/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs +++ b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs @@ -95,38 +95,70 @@ public Expression CreateMaterializeExpression( properties.Remove(consumedProperty); } - var materializationExpression = HandleMaterializationInterception(); + var constructorExpression = constructorBinding.CreateConstructorExpression(bindingInfo); + var getValueBufferExpression = Call(bindingInfo.MaterializationContextExpression, MaterializationContext.GetValueBufferMethod); + var materializationExpression = _materializationInterceptor == null + // TODO: This currently applies the materialization interceptor only on the root structural type - any contained complex types + // don't get intercepted. #35883 + || structuralType is not IEntityType + ? properties.Count == 0 && blockExpressions.Count == 0 + ? constructorExpression + : CreateMaterializeExpression(blockExpressions, instanceVariable, constructorExpression, getValueBufferExpression, properties, bindingInfo) + : CreateInterceptionMaterializeExpression( + structuralType, + properties, + _materializationInterceptor, + bindingInfo, + constructorExpression, + getValueBufferExpression, + instanceVariable, + blockExpressions); - return - structuralType is IComplexType complexType + return structuralType is IComplexType complexType && ReadComplexTypeDirectly(complexType) - && (IsNullable(complexType) || parameters.AllowNullable == true) - ? HandleNullableComplexTypeMaterialization( - complexType, - complexType.ClrType, - materializationExpression, - bindingInfo) - : materializationExpression; - - Expression HandleMaterializationInterception() + && parameters.ClrType.IsNullableType() + ? HandleNullableComplexTypeMaterialization( + complexType, + parameters.ClrType, + materializationExpression, + getValueBufferExpression) + : materializationExpression; + + // Creates a conditional expression that handles materialization of nullable complex types. + // For nullable complex types, the method checks if all scalar properties are null + // and returns default if they are, otherwise materializes the complex type instance. + // If there's a required (non-nullable) property, only that property is checked for efficiency. + Expression HandleNullableComplexTypeMaterialization( + IComplexType complexType, + Type clrType, + Expression materializeExpression, + MethodCallExpression getValueBufferExpression) { - var constructorExpression = constructorBinding.CreateConstructorExpression(bindingInfo); - - return _materializationInterceptor == null - // TODO: This currently applies the materialization interceptor only on the root structural type - any contained complex types - // don't get intercepted. #35883 - || structuralType is not IEntityType - ? properties.Count == 0 && blockExpressions.Count == 0 - ? constructorExpression - : CreateMaterializeExpression(blockExpressions, instanceVariable, constructorExpression, properties, bindingInfo) - : CreateInterceptionMaterializeExpression( - structuralType, - properties, - _materializationInterceptor, - bindingInfo, - constructorExpression, - instanceVariable, - blockExpressions); + // Get all scalar properties of the complex type (including nested ones). + var allScalarProperties = complexType.GetFlattenedProperties().ToList(); + + var requiredProperty = allScalarProperties.Where(p => !p.IsNullable).FirstOrDefault(); + var nullCheck = requiredProperty is not null + // If there's a required property, it's enough to check just that one for null. + ? Equal( + getValueBufferExpression.CreateValueBufferReadValueExpression(typeof(object), requiredProperty.GetIndex(), requiredProperty), + Constant(null, typeof(object))) + // Create null checks for all scalar properties. + : allScalarProperties + .Select(p => + Equal( + getValueBufferExpression.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p), + Constant(null, typeof(object)))) + .Aggregate(AndAlso); + + // If property/properties are null, return default (to handle structs); otherwise materialize the complex type. + return Condition( + nullCheck, + Default(clrType), + // Materialization expression is always returning a non-nullable type, so we need to convert it to nullable if necessary. + clrType.IsNullableType() + ? Convert(materializeExpression, clrType) + : materializeExpression); } } @@ -147,7 +179,7 @@ protected virtual void AddInitializeExpression( IPropertyBase property, ParameterBindingInfo bindingInfo, Expression instanceVariable, - MethodCallExpression valueBufferExpression, + MethodCallExpression getValueBufferExpression, List blockExpressions) { if (property is IComplexProperty cp && !ReadComplexTypeDirectly(cp.ComplexType)) @@ -160,7 +192,7 @@ protected virtual void AddInitializeExpression( var valueExpression = property switch { IProperty p - => valueBufferExpression.CreateValueBufferReadValueExpression(memberInfo.GetMemberType(), p.GetIndex(), p), + => getValueBufferExpression.CreateValueBufferReadValueExpression(memberInfo.GetMemberType(), p.GetIndex(), p), IServiceProperty serviceProperty => serviceProperty.ParameterBinding.BindToParameter(bindingInfo), @@ -169,7 +201,9 @@ IServiceProperty serviceProperty => Default(complexProperty.ClrType), // Initialize collections to null, they'll be populated separately IComplexProperty complexProperty - => CreateComplexTypeMaterializeExpression(complexProperty, bindingInfo), + => CreateMaterializeExpression( + new StructuralTypeMaterializerSourceParameters(complexProperty.ComplexType, "complexType", complexProperty.ClrType, QueryTrackingBehavior: null), + bindingInfo.MaterializationContextExpression), _ => throw new UnreachableException() }; @@ -217,37 +251,18 @@ static Expression CreateMemberAssignment(Expression parameter, MemberInfo member value) : MakeMemberAccess(parameter, memberInfo).Assign(value); } - - Expression CreateComplexTypeMaterializeExpression(IComplexProperty complexProperty, ParameterBindingInfo bindingInfo) - { - var materializeExpression = CreateMaterializeExpression( - new StructuralTypeMaterializerSourceParameters( - complexProperty.ComplexType, "complexType", null, QueryTrackingBehavior: null), - bindingInfo.MaterializationContextExpression); - - return IsNullable(complexProperty) - ? HandleNullableComplexTypeMaterialization( - complexProperty.ComplexType, - complexProperty.ClrType, - materializeExpression, - bindingInfo) - : materializeExpression; - } } private void AddInitializeExpressions( HashSet properties, ParameterBindingInfo bindingInfo, Expression instanceVariable, + MethodCallExpression getValueBufferExpression, List blockExpressions) { - var valueBufferExpression = Call( - bindingInfo.MaterializationContextExpression, - MaterializationContext.GetValueBufferMethod); - foreach (var property in properties) { - AddInitializeExpression(property, bindingInfo, instanceVariable, valueBufferExpression, blockExpressions); + AddInitializeExpression(property, bindingInfo, instanceVariable, getValueBufferExpression, blockExpressions); } } @@ -333,12 +348,13 @@ private Expression CreateMaterializeExpression( List blockExpressions, ParameterExpression instanceVariable, Expression constructorExpression, + MethodCallExpression getValueBufferExpression, HashSet properties, ParameterBindingInfo bindingInfo) { blockExpressions.Add(Assign(instanceVariable, constructorExpression)); - AddInitializeExpressions(properties, bindingInfo, instanceVariable, blockExpressions); + AddInitializeExpressions(properties, bindingInfo, instanceVariable, getValueBufferExpression, blockExpressions); if (bindingInfo.StructuralType is IEntityType) { @@ -356,6 +372,7 @@ private Expression CreateInterceptionMaterializeExpression( IMaterializationInterceptor materializationInterceptor, ParameterBindingInfo bindingInfo, Expression constructorExpression, + MethodCallExpression getValueBufferExpression, ParameterExpression instanceVariable, List blockExpressions) { @@ -460,8 +477,6 @@ BlockExpression CreateAccessorDictionaryExpression() { var dictionaryVariable = Variable( typeof(Dictionary)>), "dictionary"); - var valueBufferExpression = Call( - bindingInfo.MaterializationContextExpression, MaterializationContext.GetValueBufferMethod); var snapshotBlockExpressions = new List { Assign( @@ -495,12 +510,12 @@ Expression CreateAccessorReadExpression() ? serviceProperty.ParameterBinding.BindToParameter(bindingInfo) : (property as IProperty)?.IsPrimaryKey() == true ? Convert( - valueBufferExpression.CreateValueBufferReadValueExpression( + getValueBufferExpression.CreateValueBufferReadValueExpression( typeof(object), property.GetIndex(), property), property.ClrType) - : valueBufferExpression.CreateValueBufferReadValueExpression( + : getValueBufferExpression.CreateValueBufferReadValueExpression( property.ClrType, property.GetIndex(), property); @@ -516,7 +531,7 @@ BlockExpression CreateInitializeExpression() { var initializeBlockExpressions = new List(); - AddInitializeExpressions(properties, bindingInfo, instanceVariable, initializeBlockExpressions); + AddInitializeExpressions(properties, bindingInfo, instanceVariable, getValueBufferExpression, initializeBlockExpressions); if (bindingInfo.StructuralType is IEntityType) { @@ -540,7 +555,7 @@ var materializationContextParameter return Lambda>( ((IStructuralTypeMaterializerSource)this).CreateMaterializeExpression( - new StructuralTypeMaterializerSourceParameters(entityType, "instance", null, null), materializationContextParameter), + new StructuralTypeMaterializerSourceParameters(entityType, "instance", entityType.ClrType, null), materializationContextParameter), materializationContextParameter) .Compile(); } @@ -557,7 +572,7 @@ public virtual Func GetMaterializer(IComplexType return Lambda>( ((IStructuralTypeMaterializerSource)this).CreateMaterializeExpression( - new StructuralTypeMaterializerSourceParameters(complexType, "instance", null, null), materializationContextParameter), + new StructuralTypeMaterializerSourceParameters(complexType, "instance", complexType.ClrType, null), materializationContextParameter), materializationContextParameter) .Compile(); } @@ -605,15 +620,13 @@ public virtual Func GetEmptyMaterializer(IComple /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual Func GetEmptyMaterializer( - ITypeBase entityType, - InstantiationBinding binding, - List serviceProperties) + ITypeBase entityType, InstantiationBinding binding, List serviceProperties) { binding = ModifyBindings(entityType, binding); var materializationContextExpression = Parameter(typeof(MaterializationContext), "mc"); var bindingInfo = new ParameterBindingInfo( - new StructuralTypeMaterializerSourceParameters(entityType, "instance", null, null), materializationContextExpression); + new StructuralTypeMaterializerSourceParameters(entityType, "instance", entityType.ClrType, null), materializationContextExpression); var blockExpressions = new List(); var instanceVariable = Variable(binding.RuntimeType, "instance"); @@ -622,6 +635,7 @@ public virtual Func GetEmptyMaterializer( CreateServiceInstances(binding, bindingInfo, blockExpressions, serviceProperties); var constructorExpression = binding.CreateConstructorExpression(bindingInfo); + var getValueBufferExpression = Call(bindingInfo.MaterializationContextExpression, MaterializationContext.GetValueBufferMethod); var properties = new HashSet(serviceProperties); foreach (var consumedProperty in binding.ParameterBindings.SelectMany(p => p.ConsumedProperties)) @@ -634,13 +648,14 @@ public virtual Func GetEmptyMaterializer( ? properties.Count == 0 && blockExpressions.Count == 0 ? constructorExpression : CreateMaterializeExpression( - blockExpressions, instanceVariable, constructorExpression, properties, bindingInfo) + blockExpressions, instanceVariable, constructorExpression, getValueBufferExpression, properties, bindingInfo) : CreateInterceptionMaterializeExpression( entityType, [], _materializationInterceptor, bindingInfo, constructorExpression, + getValueBufferExpression, instanceVariable, blockExpressions), materializationContextExpression) @@ -686,48 +701,6 @@ private static void CreateServiceInstances( } } - private Expression HandleNullableComplexTypeMaterialization( - IComplexType complexType, - Type clrType, - Expression materializeExpression, - ParameterBindingInfo bindingInfo) - { - var valueBufferExpression = Call( - bindingInfo.MaterializationContextExpression, - MaterializationContext.GetValueBufferMethod); - - // Get all scalar properties of the complex type (including nested ones). - var allScalarProperties = complexType.GetFlattenedProperties().ToList(); - - if (allScalarProperties is []) - { - // If no scalar properties, just create the instance. - return CreateMaterializeExpression( - new StructuralTypeMaterializerSourceParameters(complexType, "complexType", null, QueryTrackingBehavior: null), - bindingInfo.MaterializationContextExpression); - } - - var requiredProperty = allScalarProperties.Where(p => !p.IsNullable).FirstOrDefault(); - var nullCheck = requiredProperty is not null - // If there's a required property, it's enough to check just that one for null. - ? Equal( - valueBufferExpression.CreateValueBufferReadValueExpression(typeof(object), requiredProperty.GetIndex(), requiredProperty), - Constant(null, typeof(object))) - // Create null checks for all scalar properties. - : allScalarProperties - .Select(p => - Equal( - valueBufferExpression.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p), - Constant(null, typeof(object)))) - .Aggregate(AndAlso); - - // If property/properties are null, return default (to handle structs); otherwise materialize the complex type. - return Condition( - nullCheck, - Default(clrType), - materializeExpression); - } - private static bool IsNullable(IComplexType complexType) => IsNullable(complexType.ComplexProperty); diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs index 1fa5bffdcd3..b9a3fe4b0c9 100644 --- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs @@ -449,7 +449,7 @@ private Expression ProcessStructuralTypeShaper(StructuralTypeShaperExpression sh var variables = new List(); var typeBase = shaper.StructuralType; - var clrType = typeBase.ClrType; + var clrType = shaper.Type; var materializationContextVariable = Variable( typeof(MaterializationContext), @@ -634,7 +634,7 @@ private Expression MaterializeEntity( typeof(ISnapshot)) : Constant(Snapshot.Empty, typeof(ISnapshot)))); - var returnType = structuralType.ClrType; + var returnType = shaper.Type; var valueBufferExpression = Call(materializationContextVariable, MaterializationContext.GetValueBufferMethod); var materializationConditionBody = ReplacingExpressionVisitor.Replace( @@ -654,7 +654,7 @@ private Expression MaterializeEntity( { var concreteStructuralType = concreteStructuralTypes[i]; switchCases[i] = SwitchCase( - CreateFullMaterializeExpression(concreteStructuralTypes[i], shaper.IsNullable, expressionContext), + CreateFullMaterializeExpression(concreteStructuralTypes[i], expressionContext), supportsPrecompiledQuery ? liftableConstantFactory.CreateLiftableConstant( concreteStructuralTypes[i], @@ -707,7 +707,6 @@ private Expression MaterializeEntity( private BlockExpression CreateFullMaterializeExpression( ITypeBase concreteStructuralType, - bool shaperIsNullable, (Type ReturnType, ParameterExpression MaterializationContextVariable, ParameterExpression ConcreteEntityTypeVariable, @@ -723,7 +722,7 @@ private BlockExpression CreateFullMaterializeExpression( var materializer = materializerSource .CreateMaterializeExpression( new StructuralTypeMaterializerSourceParameters( - concreteStructuralType, "instance", shaperIsNullable, queryTrackingBehavior), materializationContextVariable); + concreteStructuralType, "instance", returnType, queryTrackingBehavior), materializationContextVariable); // TODO: Properly support shadow properties for complex types #35613 if (_queryStateManager diff --git a/src/EFCore/Query/StructuralTypeShaperExpression.cs b/src/EFCore/Query/StructuralTypeShaperExpression.cs index 03006addc9d..5098634f810 100644 --- a/src/EFCore/Query/StructuralTypeShaperExpression.cs +++ b/src/EFCore/Query/StructuralTypeShaperExpression.cs @@ -26,6 +26,8 @@ private static readonly MethodInfo CreateUnableToDiscriminateExceptionMethod private static readonly MethodInfo GetDiscriminatorValueMethod = typeof(IReadOnlyTypeBase).GetMethod(nameof(IReadOnlyTypeBase.GetDiscriminatorValue))!; + private readonly Type _clrType; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -46,7 +48,7 @@ public StructuralTypeShaperExpression( ITypeBase type, Expression valueBufferExpression, bool nullable) - : this(type, valueBufferExpression, nullable, null) + : this(type, valueBufferExpression, nullable, null, type.ClrType) { } @@ -59,11 +61,13 @@ public StructuralTypeShaperExpression( /// /// An expression of to determine which structural type to materialize. /// + /// CLR type for this expression as returned from . protected StructuralTypeShaperExpression( ITypeBase type, Expression valueBufferExpression, bool nullable, - LambdaExpression? materializationCondition) + LambdaExpression? materializationCondition, + Type clrType) { if (materializationCondition == null) { @@ -80,6 +84,9 @@ protected StructuralTypeShaperExpression( ValueBufferExpression = valueBufferExpression; IsNullable = nullable; MaterializationCondition = materializationCondition!; + Check.DebugAssert(clrType == StructuralType.ClrType || clrType == StructuralType.ClrType.MakeNullable(), + $"The CLR type '{clrType}' must be equal to the structural type '{StructuralType.ClrType}' or a nullable version of it."); + _clrType = clrType; } /// @@ -235,7 +242,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// This expression if the type was not changed, or a new expression with the updated type. public virtual StructuralTypeShaperExpression WithType(ITypeBase type) => type != StructuralType - ? new StructuralTypeShaperExpression(type, ValueBufferExpression, IsNullable, materializationCondition: null) + ? new StructuralTypeShaperExpression(type, ValueBufferExpression, IsNullable, materializationCondition: null, type.ClrType) : this; /// @@ -246,7 +253,7 @@ public virtual StructuralTypeShaperExpression WithType(ITypeBase type) public virtual StructuralTypeShaperExpression MakeNullable(bool nullable = true) => IsNullable != nullable // Marking nullable requires re-computation of materialization condition - ? new StructuralTypeShaperExpression(StructuralType, ValueBufferExpression, nullable, materializationCondition: null) + ? new StructuralTypeShaperExpression(StructuralType, ValueBufferExpression, nullable, materializationCondition: null, Type) : this; /// @@ -257,12 +264,30 @@ public virtual StructuralTypeShaperExpression MakeNullable(bool nullable = true) /// This expression if no children changed, or an expression with the updated children. public virtual StructuralTypeShaperExpression Update(Expression valueBufferExpression) => valueBufferExpression != ValueBufferExpression - ? new StructuralTypeShaperExpression(StructuralType, valueBufferExpression, IsNullable, MaterializationCondition) + ? new StructuralTypeShaperExpression(StructuralType, valueBufferExpression, IsNullable, MaterializationCondition, Type) : this; /// public override Type Type - => StructuralType.ClrType; + => _clrType; + + /// + /// Changes the of this expression to be nullable. + /// + [EntityFrameworkInternal] + public virtual StructuralTypeShaperExpression MakeClrTypeNullable() + => Type != Type.MakeNullable() + ? new StructuralTypeShaperExpression(StructuralType, ValueBufferExpression, IsNullable, MaterializationCondition, Type.MakeNullable()) + : this; + + /// + /// Changes the of this expression to be non nullable. + /// + [EntityFrameworkInternal] + public virtual StructuralTypeShaperExpression MakeClrTypeNonNullable() + => Type != Type.UnwrapNullableType() + ? new StructuralTypeShaperExpression(StructuralType, ValueBufferExpression, IsNullable, MaterializationCondition, Type.UnwrapNullableType()) + : this; /// public sealed override ExpressionType NodeType diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs index c83866193b0..74e5f4ff677 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs @@ -190,9 +190,9 @@ FROM root c } } - [ConditionalTheory(Skip = "This type of projection does not make sense for Cosmos.")] public override Task Select_required_related_via_optional_navigation(QueryTrackingBehavior queryTrackingBehavior) - => base.Select_required_related_via_optional_navigation(queryTrackingBehavior); + // We don't support (inter-document) navigations with Cosmos. + => Assert.ThrowsAsync(() => base.Select_required_related_via_optional_navigation(queryTrackingBehavior)); #endregion Non-collection diff --git a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs index 02543feb6b2..23062c6dc78 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs @@ -297,6 +297,12 @@ public virtual Task Project_struct_complex_type_via_optional_navigation(bool asy ss => ss.Set().Select(cg => cg.OptionalCustomer!.ShippingAddress), ss => ss.Set().Select(cg => cg.OptionalCustomer != null ? cg.OptionalCustomer.ShippingAddress : default)); + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public virtual Task Project_nullable_struct_complex_type_via_optional_navigation(bool async) + => AssertQuery( + async, + ss => ss.Set().Select(cg => cg.OptionalCustomer != null ? cg.OptionalCustomer.ShippingAddress : (AddressStruct?)null)); + [ConditionalTheory, MemberData(nameof(IsAsyncData))] public virtual Task Project_struct_complex_type_via_required_navigation(bool async) => AssertQuery( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs index 7b31606e0e9..fb794096cae 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs @@ -483,6 +483,18 @@ FROM [ValuedCustomerGroup] AS [v] """); } + public override async Task Project_nullable_struct_complex_type_via_optional_navigation(bool async) + { + await base.Project_nullable_struct_complex_type_via_optional_navigation(async); + + AssertSql( + """ +SELECT [v0].[ShippingAddress_AddressLine1], [v0].[ShippingAddress_AddressLine2], [v0].[ShippingAddress_ZipCode], [v0].[ShippingAddress_Country_Code], [v0].[ShippingAddress_Country_FullName] +FROM [ValuedCustomerGroup] AS [v] +LEFT JOIN [ValuedCustomer] AS [v0] ON [v].[OptionalCustomerId] = [v0].[Id] +"""); + } + public override async Task Project_struct_complex_type_via_required_navigation(bool async) { await base.Project_struct_complex_type_via_required_navigation(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqlServerTest.cs index bc12ee0ef53..dc2c4c98ed2 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqlServerTest.cs @@ -52,7 +52,7 @@ public override async Task Related_with_inline_null() """ SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] FROM [RootEntity] AS [r] -WHERE [r].[OptionalRelated_Id] IS NULL AND [r].[OptionalRelated_Int] IS NULL AND [r].[OptionalRelated_Name] IS NULL AND [r].[OptionalRelated_String] IS NULL AND [r].[OptionalRelated_OptionalNested_Id] IS NULL AND [r].[OptionalRelated_OptionalNested_Int] IS NULL AND [r].[OptionalRelated_OptionalNested_Name] IS NULL AND [r].[OptionalRelated_OptionalNested_String] IS NULL AND [r].[OptionalRelated_RequiredNested_Id] IS NULL AND [r].[OptionalRelated_RequiredNested_Int] IS NULL AND [r].[OptionalRelated_RequiredNested_Name] IS NULL AND [r].[OptionalRelated_RequiredNested_String] IS NULL +WHERE [r].[OptionalRelated_Id] IS NULL """); } @@ -76,7 +76,7 @@ public override async Task Nested_with_inline_null() """ SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] FROM [RootEntity] AS [r] -WHERE [r].[RequiredRelated_OptionalNested_Id] IS NULL AND [r].[RequiredRelated_OptionalNested_Int] IS NULL AND [r].[RequiredRelated_OptionalNested_Name] IS NULL AND [r].[RequiredRelated_OptionalNested_String] IS NULL +WHERE [r].[RequiredRelated_OptionalNested_Id] IS NULL """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs index 7a9a221c9a7..3d93abac753 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs @@ -483,6 +483,18 @@ public override async Task Project_struct_complex_type_via_optional_navigation(b """); } + public override async Task Project_nullable_struct_complex_type_via_optional_navigation(bool async) + { + await base.Project_nullable_struct_complex_type_via_optional_navigation(async); + + AssertSql( + """ +SELECT "v0"."ShippingAddress_AddressLine1", "v0"."ShippingAddress_AddressLine2", "v0"."ShippingAddress_ZipCode", "v0"."ShippingAddress_Country_Code", "v0"."ShippingAddress_Country_FullName" +FROM "ValuedCustomerGroup" AS "v" +LEFT JOIN "ValuedCustomer" AS "v0" ON "v"."OptionalCustomerId" = "v0"."Id" +"""); + } + public override async Task Project_struct_complex_type_via_required_navigation(bool async) { await base.Project_struct_complex_type_via_required_navigation(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs index 92dcd0cebf5..5a74e7598ea 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs @@ -7,44 +7,5 @@ public class ComplexTableSplittingMiscellaneousSqliteTest( ComplexTableSplittingSqliteFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTableSplittingMiscellaneousRelationalTestBase(fixture, testOutputHelper) -{ - public override async Task Where_related_property() - { - await base.Where_related_property(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_Int" = 8 -"""); - } - - public override async Task Where_optional_related_property() - { - await base.Where_optional_related_property(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."OptionalRelated_Int" = 8 -"""); - } - - public override async Task Where_nested_related_property() - { - await base.Where_nested_related_property(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_RequiredNested_Int" = 8 -"""); - } - - [ConditionalFact] - public virtual void Check_all_tests_overridden() - => TestHelpers.AssertAllMethodsOverridden(GetType()); +{ } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs index c9ad7ff74b5..bb68fa057be 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs @@ -8,216 +8,12 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplittin public class ComplexTableSplittingProjectionSqliteTest(ComplexTableSplittingSqliteFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTableSplittingProjectionRelationalTestBase(fixture, testOutputHelper) { - public override async Task Select_related_collection(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_related_collection(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -ORDER BY "r"."Id" -"""); - } - - public override async Task Select_nested_collection_on_required_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_nested_collection_on_required_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -ORDER BY "r"."Id" -"""); - } - - public override async Task Select_root(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_root(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_property_on_required_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_property_on_required_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."RequiredRelated_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_property_on_optional_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_property_on_optional_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."OptionalRelated_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_value_type_property_on_null_related_throws(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_value_type_property_on_null_related_throws(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."OptionalRelated_Int" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_nullable_value_type_property_on_null_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_nullable_value_type_property_on_null_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."OptionalRelated_Int" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_optional_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_optional_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_required_nested_on_required_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_required_nested_on_required_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_optional_nested_on_required_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_optional_nested_on_required_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_required_nested_on_optional_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_required_nested_on_optional_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_optional_nested_on_optional_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_optional_nested_on_optional_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String" -FROM "RootEntity" AS "r" -"""); - } - - public override async Task Select_required_related_via_optional_navigation(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_required_related_via_optional_navigation(queryTrackingBehavior); - - AssertSql(""" -SELECT "r0"."RequiredRelated_Id", "r0"."RequiredRelated_Int", "r0"."RequiredRelated_Name", "r0"."RequiredRelated_String", "r0"."RequiredRelated_OptionalNested_Id", "r0"."RequiredRelated_OptionalNested_Int", "r0"."RequiredRelated_OptionalNested_Name", "r0"."RequiredRelated_OptionalNested_String", "r0"."RequiredRelated_RequiredNested_Id", "r0"."RequiredRelated_RequiredNested_Int", "r0"."RequiredRelated_RequiredNested_Name", "r0"."RequiredRelated_RequiredNested_String" -FROM "RootReferencingEntity" AS "r" -LEFT JOIN "RootEntity" AS "r0" ON "r"."RootEntityId" = "r0"."Id" -"""); - } - - public override async Task Select_nested_collection_on_optional_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_nested_collection_on_optional_related(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -ORDER BY "r"."Id" -"""); - } - - public override async Task SelectMany_related_collection(QueryTrackingBehavior queryTrackingBehavior) - { - await base.SelectMany_related_collection(queryTrackingBehavior); - - AssertSql(); - } - - public override async Task SelectMany_nested_collection_on_required_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.SelectMany_nested_collection_on_required_related(queryTrackingBehavior); - - AssertSql(); - } - - public override async Task SelectMany_nested_collection_on_optional_related(QueryTrackingBehavior queryTrackingBehavior) - { - await base.SelectMany_nested_collection_on_optional_related(queryTrackingBehavior); - - AssertSql(); - } - - public override async Task Select_root_duplicated(QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_root_duplicated(queryTrackingBehavior); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -"""); - } - public override Task Select_subquery_required_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(queryTrackingBehavior)); public override Task Select_subquery_optional_related_FirstOrDefault(QueryTrackingBehavior queryTrackingBehavior) => AssertApplyNotSupported(() => base.Select_subquery_optional_related_FirstOrDefault(queryTrackingBehavior)); - [ConditionalFact] - public virtual void Check_all_tests_overridden() - => TestHelpers.AssertAllMethodsOverridden(GetType()); - private static async Task AssertApplyNotSupported(Func query) => Assert.Equal( SqliteStrings.ApplyNotSupported, diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqliteTest.cs index be0e7050262..8fab931eb9e 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingStructuralEqualitySqliteTest.cs @@ -8,129 +8,4 @@ public class ComplexTableSplittingStructuralEqualitySqliteTest( ITestOutputHelper testOutputHelper) : ComplexTableSplittingStructuralEqualityRelationalTestBase(fixture, testOutputHelper) { - public override async Task Two_related() - { - await base.Two_related(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_Id" = "r"."OptionalRelated_Id" AND "r"."RequiredRelated_Int" = "r"."OptionalRelated_Int" AND "r"."RequiredRelated_Name" = "r"."OptionalRelated_Name" AND "r"."RequiredRelated_String" = "r"."OptionalRelated_String" AND ("r"."RequiredRelated_OptionalNested_Id" = "r"."RequiredRelated_OptionalNested_Id" OR "r"."RequiredRelated_OptionalNested_Id" IS NULL) AND ("r"."RequiredRelated_OptionalNested_Int" = "r"."RequiredRelated_OptionalNested_Int" OR "r"."RequiredRelated_OptionalNested_Int" IS NULL) AND ("r"."RequiredRelated_OptionalNested_Name" = "r"."RequiredRelated_OptionalNested_Name" OR "r"."RequiredRelated_OptionalNested_Name" IS NULL) AND ("r"."RequiredRelated_OptionalNested_String" = "r"."RequiredRelated_OptionalNested_String" OR "r"."RequiredRelated_OptionalNested_String" IS NULL) AND "r"."RequiredRelated_RequiredNested_Id" = "r"."RequiredRelated_RequiredNested_Id" AND "r"."RequiredRelated_RequiredNested_Int" = "r"."RequiredRelated_RequiredNested_Int" AND "r"."RequiredRelated_RequiredNested_Name" = "r"."RequiredRelated_RequiredNested_Name" AND "r"."RequiredRelated_RequiredNested_String" = "r"."RequiredRelated_RequiredNested_String" -"""); - } - - public override async Task Two_nested() - { - await base.Two_nested(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_RequiredNested_Id" = "r"."OptionalRelated_RequiredNested_Id" AND "r"."RequiredRelated_RequiredNested_Int" = "r"."OptionalRelated_RequiredNested_Int" AND "r"."RequiredRelated_RequiredNested_Name" = "r"."OptionalRelated_RequiredNested_Name" AND "r"."RequiredRelated_RequiredNested_String" = "r"."OptionalRelated_RequiredNested_String" -"""); - } - - public override async Task Not_equals() - { - await base.Not_equals(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_Id" <> "r"."OptionalRelated_Id" OR "r"."OptionalRelated_Id" IS NULL OR "r"."RequiredRelated_Int" <> "r"."OptionalRelated_Int" OR "r"."OptionalRelated_Int" IS NULL OR "r"."RequiredRelated_Name" <> "r"."OptionalRelated_Name" OR "r"."OptionalRelated_Name" IS NULL OR "r"."RequiredRelated_String" <> "r"."OptionalRelated_String" OR "r"."OptionalRelated_String" IS NULL OR (("r"."RequiredRelated_OptionalNested_Id" <> "r"."RequiredRelated_OptionalNested_Id" OR "r"."RequiredRelated_OptionalNested_Id" IS NULL) AND "r"."RequiredRelated_OptionalNested_Id" IS NOT NULL) OR (("r"."RequiredRelated_OptionalNested_Int" <> "r"."RequiredRelated_OptionalNested_Int" OR "r"."RequiredRelated_OptionalNested_Int" IS NULL) AND "r"."RequiredRelated_OptionalNested_Int" IS NOT NULL) OR (("r"."RequiredRelated_OptionalNested_Name" <> "r"."RequiredRelated_OptionalNested_Name" OR "r"."RequiredRelated_OptionalNested_Name" IS NULL) AND "r"."RequiredRelated_OptionalNested_Name" IS NOT NULL) OR (("r"."RequiredRelated_OptionalNested_String" <> "r"."RequiredRelated_OptionalNested_String" OR "r"."RequiredRelated_OptionalNested_String" IS NULL) AND "r"."RequiredRelated_OptionalNested_String" IS NOT NULL) OR "r"."RequiredRelated_RequiredNested_Id" <> "r"."RequiredRelated_RequiredNested_Id" OR "r"."RequiredRelated_RequiredNested_Id" IS NULL OR "r"."RequiredRelated_RequiredNested_Int" <> "r"."RequiredRelated_RequiredNested_Int" OR "r"."RequiredRelated_RequiredNested_Int" IS NULL OR "r"."RequiredRelated_RequiredNested_Name" <> "r"."RequiredRelated_RequiredNested_Name" OR "r"."RequiredRelated_RequiredNested_Name" IS NULL OR "r"."RequiredRelated_RequiredNested_String" <> "r"."RequiredRelated_RequiredNested_String" OR "r"."RequiredRelated_RequiredNested_String" IS NULL -"""); - } - - public override async Task Related_with_inline_null() - { - await base.Related_with_inline_null(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."OptionalRelated_Id" IS NULL AND "r"."OptionalRelated_Int" IS NULL AND "r"."OptionalRelated_Name" IS NULL AND "r"."OptionalRelated_String" IS NULL AND "r"."OptionalRelated_OptionalNested_Id" IS NULL AND "r"."OptionalRelated_OptionalNested_Int" IS NULL AND "r"."OptionalRelated_OptionalNested_Name" IS NULL AND "r"."OptionalRelated_OptionalNested_String" IS NULL AND "r"."OptionalRelated_RequiredNested_Id" IS NULL AND "r"."OptionalRelated_RequiredNested_Int" IS NULL AND "r"."OptionalRelated_RequiredNested_Name" IS NULL AND "r"."OptionalRelated_RequiredNested_String" IS NULL -"""); - } - - public override async Task Related_with_parameter_null() - { - await base.Related_with_parameter_null(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."OptionalRelated_Id" IS NULL AND "r"."OptionalRelated_Int" IS NULL AND "r"."OptionalRelated_Name" IS NULL AND "r"."OptionalRelated_String" IS NULL AND "r"."OptionalRelated_OptionalNested_Id" IS NULL AND "r"."OptionalRelated_OptionalNested_Int" IS NULL AND "r"."OptionalRelated_OptionalNested_Name" IS NULL AND "r"."OptionalRelated_OptionalNested_String" IS NULL AND "r"."OptionalRelated_RequiredNested_Id" IS NULL AND "r"."OptionalRelated_RequiredNested_Int" IS NULL AND "r"."OptionalRelated_RequiredNested_Name" IS NULL AND "r"."OptionalRelated_RequiredNested_String" IS NULL -"""); - } - - public override async Task Nested_with_inline_null() - { - await base.Nested_with_inline_null(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_OptionalNested_Id" IS NULL AND "r"."RequiredRelated_OptionalNested_Int" IS NULL AND "r"."RequiredRelated_OptionalNested_Name" IS NULL AND "r"."RequiredRelated_OptionalNested_String" IS NULL -"""); - } - - public override async Task Nested_with_inline() - { - await base.Nested_with_inline(); - - AssertSql( - """ -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_RequiredNested_Id" = 1000 AND "r"."RequiredRelated_RequiredNested_Int" = 8 AND "r"."RequiredRelated_RequiredNested_Name" = 'Root1_RequiredRelated_RequiredNested' AND "r"."RequiredRelated_RequiredNested_String" = 'foo' -"""); - } - - public override async Task Nested_with_parameter() - { - await base.Nested_with_parameter(); - - AssertSql( - """ -@entity_equality_nested_Id='?' (DbType = Int32) -@entity_equality_nested_Int='?' (DbType = Int32) -@entity_equality_nested_Name='?' (Size = 36) -@entity_equality_nested_String='?' (Size = 3) - -SELECT "r"."Id", "r"."Name", "r"."OptionalRelated_Id", "r"."OptionalRelated_Int", "r"."OptionalRelated_Name", "r"."OptionalRelated_String", "r"."OptionalRelated_OptionalNested_Id", "r"."OptionalRelated_OptionalNested_Int", "r"."OptionalRelated_OptionalNested_Name", "r"."OptionalRelated_OptionalNested_String", "r"."OptionalRelated_RequiredNested_Id", "r"."OptionalRelated_RequiredNested_Int", "r"."OptionalRelated_RequiredNested_Name", "r"."OptionalRelated_RequiredNested_String", "r"."RequiredRelated_Id", "r"."RequiredRelated_Int", "r"."RequiredRelated_Name", "r"."RequiredRelated_String", "r"."RequiredRelated_OptionalNested_Id", "r"."RequiredRelated_OptionalNested_Int", "r"."RequiredRelated_OptionalNested_Name", "r"."RequiredRelated_OptionalNested_String", "r"."RequiredRelated_RequiredNested_Id", "r"."RequiredRelated_RequiredNested_Int", "r"."RequiredRelated_RequiredNested_Name", "r"."RequiredRelated_RequiredNested_String" -FROM "RootEntity" AS "r" -WHERE "r"."RequiredRelated_RequiredNested_Id" = @entity_equality_nested_Id AND "r"."RequiredRelated_RequiredNested_Int" = @entity_equality_nested_Int AND "r"."RequiredRelated_RequiredNested_Name" = @entity_equality_nested_Name AND "r"."RequiredRelated_RequiredNested_String" = @entity_equality_nested_String -"""); - } - - public override async Task Two_nested_collections() - { - await base.Two_nested_collections(); - - AssertSql(); - } - - public override async Task Nested_collection_with_inline() - { - await base.Nested_collection_with_inline(); - - AssertSql(); - } - - public override async Task Nested_collection_with_parameter() - { - await base.Nested_collection_with_parameter(); - - AssertSql(); - } - - [ConditionalFact] - public virtual void Check_all_tests_overridden() - => TestHelpers.AssertAllMethodsOverridden(GetType()); } diff --git a/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs b/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs index 3b27f7be65b..1afef984c26 100644 --- a/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs +++ b/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs @@ -26,7 +26,7 @@ public void Throws_for_abstract_types() CoreStrings.CannotMaterializeAbstractType(nameof(SomeAbstractEntity)), Assert.Throws( () => source.CreateMaterializeExpression( - new StructuralTypeMaterializerSourceParameters((IEntityType)entityType, "", null, null), null!)) + new StructuralTypeMaterializerSourceParameters((IEntityType)entityType, "", entityType.ClrType, null), null!)) .Message); } @@ -550,7 +550,7 @@ public virtual Func GetMaterializer( IReadOnlyEntityType entityType) => Expression.Lambda>( source.CreateMaterializeExpression( - new StructuralTypeMaterializerSourceParameters((IEntityType)entityType, "instance", null, null), + new StructuralTypeMaterializerSourceParameters((IEntityType)entityType, "instance", entityType.ClrType, null), _contextParameter), _contextParameter) .Compile();