Skip to content

Commit

Permalink
Query: DRY code for materialization condition
Browse files Browse the repository at this point in the history
  • Loading branch information
smitpatel committed Mar 20, 2020
1 parent ead70ba commit 7166201
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 92 deletions.
123 changes: 32 additions & 91 deletions src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,20 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Query
{
public class RelationalEntityShaperExpression : EntityShaperExpression
{
private static readonly MethodInfo _createUnableToDiscriminateException
= typeof(RelationalEntityShaperExpression).GetTypeInfo()
.GetDeclaredMethod(nameof(CreateUnableToDiscriminateException));

[UsedImplicitly]
private static Exception CreateUnableToDiscriminateException(IEntityType entityType, object discriminator)
=> new InvalidOperationException(CoreStrings.UnableToDiscriminate(entityType.DisplayName(), discriminator?.ToString()));
public RelationalEntityShaperExpression([NotNull] IEntityType entityType, [NotNull] Expression valueBufferExpression, bool nullable)
: base(entityType, valueBufferExpression, nullable,
GenerateMaterializationCondition(Check.NotNull(entityType, nameof(entityType)), nullable))
: base(entityType, valueBufferExpression, nullable, null)
{
}

Expand All @@ -36,105 +24,58 @@ public RelationalEntityShaperExpression(
[NotNull] Expression valueBufferExpression,
bool nullable,
[CanBeNull] LambdaExpression materializationCondition)
: base(entityType, valueBufferExpression, nullable,
materializationCondition ?? GenerateMaterializationCondition(Check.NotNull(entityType, nameof(entityType)), nullable))
: base(entityType, valueBufferExpression, nullable, materializationCondition)
{
}

private static LambdaExpression GenerateMaterializationCondition(IEntityType entityType, bool nullable)
protected override LambdaExpression GenerateMaterializationCondition(IEntityType entityType, bool nullable)
{
var valueBufferParameter = Parameter(typeof(ValueBuffer));
Check.NotNull(entityType, nameof(EntityType));

var baseCondition = base.GenerateMaterializationCondition(entityType, nullable);

var keyless = entityType.FindPrimaryKey() == null;
var optionalDependent = false;
if (!keyless)
if (entityType.FindPrimaryKey() != null)
{
var linkingFks = entityType.GetViewOrTableMappings().Single().Table.GetInternalForeignKeys(entityType);
if (linkingFks != null
&& linkingFks.Any())
{
optionalDependent = true;
}
}

Expression body;
var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToArray();
var discriminatorProperty = entityType.GetDiscriminatorProperty();
if (discriminatorProperty != null)
{
var discriminatorValueVariable = Variable(discriminatorProperty.ClrType, "discriminator");
var expressions = new List<Expression>
{
Assign(
discriminatorValueVariable,
valueBufferParameter.CreateValueBufferReadValueExpression(
discriminatorProperty.ClrType, discriminatorProperty.GetIndex(), discriminatorProperty))
};

var switchCases = new SwitchCase[concreteEntityTypes.Length];
for (var i = 0; i < concreteEntityTypes.Length; i++)
{
var discriminatorValue = Constant(concreteEntityTypes[i].GetDiscriminatorValue(), discriminatorProperty.ClrType);
switchCases[i] = SwitchCase(Constant(concreteEntityTypes[i], typeof(IEntityType)), discriminatorValue);
}

var defaultBlock = Block(
Throw(Call(
_createUnableToDiscriminateException, Constant(entityType), Convert(discriminatorValueVariable, typeof(object)))),
Default(typeof(IEntityType)));

expressions.Add(Switch(discriminatorValueVariable, defaultBlock, switchCases));
body = Block(new[] { discriminatorValueVariable }, expressions);
}
else
{
body = Constant(concreteEntityTypes.Length == 1 ? concreteEntityTypes[0] : entityType, typeof(IEntityType));
}

if (optionalDependent)
{
var requiredNonPkProperties = entityType.GetProperties().Where(p => !p.IsNullable && !p.IsPrimaryKey()).ToList();
if (requiredNonPkProperties.Count > 0)
{
body = Condition(
requiredNonPkProperties
.Select(p => NotEqual(
valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p),
Constant(null)))
.Aggregate((a, b) => AndAlso(a, b)),
body,
Default(typeof(IEntityType)));
}
else
{
var allNonPkProperties = entityType.GetProperties().Where(p => !p.IsPrimaryKey()).ToList();
if (allNonPkProperties.Count > 0)
// Optional dependent
var body = baseCondition.Body;
var valueBufferParameter = baseCondition.Parameters[0];
var requiredNonPkProperties = entityType.GetProperties().Where(p => !p.IsNullable && !p.IsPrimaryKey()).ToList();
if (requiredNonPkProperties.Count > 0)
{
body = Condition(
allNonPkProperties
requiredNonPkProperties
.Select(p => NotEqual(
valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p),
Constant(null)))
.Aggregate((a, b) => OrElse(a, b)),
.Aggregate((a, b) => AndAlso(a, b)),
body,
Default(typeof(IEntityType)));
}
else
{
var allNonPkProperties = entityType.GetProperties().Where(p => !p.IsPrimaryKey()).ToList();
if (allNonPkProperties.Count > 0)
{
body = Condition(
allNonPkProperties
.Select(p => NotEqual(
valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p),
Constant(null)))
.Aggregate((a, b) => OrElse(a, b)),
body,
Default(typeof(IEntityType)));
}
}

return Lambda(body, valueBufferParameter);
}
}
else if (keyless
&& nullable)
{
body = Condition(
entityType.GetProperties()
.Select(p => NotEqual(
valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p),
Constant(null)))
.Aggregate((a, b) => OrElse(a, b)),
body,
Default(typeof(IEntityType)));
}

return Lambda(body, valueBufferParameter);
return baseCondition;
}

public override EntityShaperExpression WithEntityType(IEntityType entityType)
Expand Down
4 changes: 3 additions & 1 deletion src/EFCore/Query/EntityShaperExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ protected EntityShaperExpression(
MaterializationCondition = materializationCondition;
}

private LambdaExpression GenerateMaterializationCondition(IEntityType entityType, bool nullable)
protected virtual LambdaExpression GenerateMaterializationCondition([NotNull] IEntityType entityType, bool nullable)
{
Check.NotNull(entityType, nameof(EntityType));

var valueBufferParameter = Parameter(typeof(ValueBuffer));
Expression body;
var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToArray();
Expand Down

0 comments on commit 7166201

Please sign in to comment.