Skip to content

Commit

Permalink
Add support for shadow properties in tracked entities
Browse files Browse the repository at this point in the history
  • Loading branch information
smitpatel committed Apr 10, 2019
1 parent 5edad88 commit 4ec63f8
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ protected override Expression CreateReadShadowValueExpression(
Expression.Call(
parameter,
ValueBuffer.GetValueMethod,
Expression.Constant(property.GetIndex())),
Expression.Constant(property.GetShadowIndex())),
property.ClrType);

/// <summary>
Expand Down
95 changes: 70 additions & 25 deletions src/EFCore/Query/PipeLine/ShapedQueryExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Query.Pipeline
Expand Down Expand Up @@ -100,7 +101,7 @@ private static readonly MethodInfo _tryGetEntryMethodInfo
= typeof(IStateManager).GetTypeInfo().GetDeclaredMethods(nameof(IStateManager.TryGetEntry))
.Single(mi => mi.GetParameters().Length == 4);
private static readonly MethodInfo _startTrackingMethodInfo
= typeof(QueryContext).GetMethod(nameof(QueryContext.StartTracking), new[] { typeof(IEntityType), typeof(object) });
= typeof(QueryContext).GetMethod(nameof(QueryContext.StartTracking), new[] { typeof(IEntityType), typeof(object), typeof(ValueBuffer) });

private readonly IEntityMaterializerSource _entityMaterializerSource;
private readonly bool _trackQueryResults;
Expand Down Expand Up @@ -167,13 +168,13 @@ protected override Expression VisitExtension(Expression extensionExpression)
_tryGetEntryMethodInfo,
Expression.Constant(primaryKey),
Expression.NewArrayInit(
typeof(object),
primaryKey.Properties
.Select(p => _entityMaterializerSource.CreateReadValueExpression(
entityShaperExpression.ValueBufferExpression,
typeof(object),
p.GetIndex(),
p))),
typeof(object),
primaryKey.Properties
.Select(p => _entityMaterializerSource.CreateReadValueExpression(
entityShaperExpression.ValueBufferExpression,
typeof(object),
p.GetIndex(),
p))),
Expression.Constant(!entityShaperExpression.Nullable),
hasNullKey)));

Expand Down Expand Up @@ -207,7 +208,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
p.GetIndex(),
p),
Expression.Constant(null)))
.Aggregate((a,b) => Expression.OrElse(a, b)),
.Aggregate((a, b) => Expression.OrElse(a, b)),
Expression.Constant(null, entityType.ClrType),
MaterializeEntity(entityType, valueBuffer))));
}
Expand Down Expand Up @@ -255,31 +256,75 @@ private Expression MaterializeEntity(IEntityType entityType, Expression valueBuf
_dbContextMemberInfo))));

var materializationExpression
= (BlockExpression)_entityMaterializerSource.CreateMaterializeExpression(
= _entityMaterializerSource.CreateMaterializeExpression(
entityType,
"instance" + _currentEntityIndex,
materializationContext);

expressions.AddRange(materializationExpression.Expressions.Take(materializationExpression.Expressions.Count - 1));

if (_trackQueryResults)
if (materializationExpression is BlockExpression blockExpression)
{
expressions.Add(
Expression.Call(
QueryCompilationContext2.QueryContextParameter,
_startTrackingMethodInfo,
Expression.Constant(entityType),
materializationExpression.Expressions.Last()));
expressions.AddRange(blockExpression.Expressions.Take(blockExpression.Expressions.Count - 1));

if (_trackQueryResults)
{
expressions.Add(
Expression.Call(
QueryCompilationContext2.QueryContextParameter,
_startTrackingMethodInfo,
Expression.Constant(entityType),
blockExpression.Expressions.Last(),
Expression.New(
typeof(ValueBuffer).GetTypeInfo().DeclaredConstructors.Single(ci => ci.GetParameters().Length == 1),
Expression.NewArrayInit(
typeof(object),
entityType.GetProperties().Where(p => p.IsShadowProperty())
.Select(p => _entityMaterializerSource.CreateReadValueExpression(
valueBuffer,
typeof(object),
p.GetIndex(),
p))))));
}

expressions.Add(blockExpression.Expressions.Last());

return Expression.Block(
entityType.ClrType,
new[] { materializationContext }.Concat(blockExpression.Variables),
expressions);
}
else
{
var instanceVariable = Expression.Variable(materializationExpression.Type, "instance" + _currentEntityIndex);
expressions.Add(Expression.Assign(instanceVariable, materializationExpression));

expressions.Add(materializationExpression.Expressions.Last());
if (_trackQueryResults)
{
expressions.Add(
Expression.Call(
QueryCompilationContext2.QueryContextParameter,
_startTrackingMethodInfo,
Expression.Constant(entityType),
instanceVariable,
Expression.New(
typeof(ValueBuffer).GetTypeInfo().DeclaredConstructors.Single(ci => ci.GetParameters().Length == 1),
Expression.NewArrayInit(
typeof(object),
entityType.GetProperties().Where(p => p.IsShadowProperty())
.Select(p => _entityMaterializerSource.CreateReadValueExpression(
valueBuffer,
typeof(object),
p.GetIndex(),
p))))));
}

expressions.Add(instanceVariable);

return Expression.Block(
entityType.ClrType,
new[] { materializationContext }.Concat(materializationExpression.Variables),
expressions);
return Expression.Block(
entityType.ClrType,
new[] { materializationContext, instanceVariable },
expressions);
}
}
}
}

}
7 changes: 3 additions & 4 deletions src/EFCore/Query/QueryContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,12 @@ public virtual void StartTracking(
}
}

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public virtual void StartTracking(
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
IEntityType entityType,
object entity)
object entity,
ValueBuffer valueBuffer)
{
StateManager.StartTrackingFromQuery(entityType, entity, ValueBuffer.Empty, handledForeignKeys: null);
StateManager.StartTrackingFromQuery(entityType, entity, valueBuffer, handledForeignKeys: null);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion test/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

<PropertyGroup>
<StandardTestTfms>netcoreapp3.0</StandardTestTfms>
<StandardTestTfms Condition="'$(DeveloperBuild)' != 'True'">net461;$(StandardTestTfms)</StandardTestTfms>
<StandardTestTfms Condition="'$(DeveloperBuild)' != 'True'">$(StandardTestTfms)</StandardTestTfms>
</PropertyGroup>
</Project>
10 changes: 5 additions & 5 deletions test/EFCore.Tests/ChangeTracking/ChangeTrackerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void Detect_property_change_is_logged(bool sensitive)
}
}

[Theory]
[Theory(Skip = "TaskList#19")]
[InlineData(false)]
[InlineData(true)]
public void Detect_foreign_key_property_change_is_logged(bool sensitive)
Expand Down Expand Up @@ -121,7 +121,7 @@ public void Detect_foreign_key_property_change_is_logged(bool sensitive)
}
}

[Theory]
[Theory(Skip = "TaskList#19")]
[InlineData(false)]
[InlineData(true)]
public void Detect_collection_change_is_logged(bool sensitive)
Expand Down Expand Up @@ -161,7 +161,7 @@ public void Detect_collection_change_is_logged(bool sensitive)
}
}

[Theory]
[Theory(Skip = "TaskList#19")]
[InlineData(false)]
[InlineData(true)]
public void Detect_reference_change_is_logged(bool sensitive)
Expand Down Expand Up @@ -352,7 +352,7 @@ public void Reset(bool generateTemporaryValues)
}
}

[Theory]
[Theory(Skip = "TaskList#19")]
[InlineData(false, CascadeTiming.OnSaveChanges, CascadeTiming.OnSaveChanges)]
[InlineData(false, CascadeTiming.OnSaveChanges, CascadeTiming.Immediate)]
[InlineData(false, CascadeTiming.OnSaveChanges, CascadeTiming.Never)]
Expand Down Expand Up @@ -471,7 +471,7 @@ void ClearMessages()
}
}

[Theory]
[Theory(Skip = "TaskList#19")]
[InlineData(false, CascadeTiming.OnSaveChanges, CascadeTiming.OnSaveChanges)]
[InlineData(false, CascadeTiming.OnSaveChanges, CascadeTiming.Immediate)]
[InlineData(false, CascadeTiming.OnSaveChanges, CascadeTiming.Never)]
Expand Down
4 changes: 2 additions & 2 deletions test/EFCore.Tests/ChangeTracking/Internal/FixupTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2414,7 +2414,7 @@ public void Replace_dependent_one_to_one_no_navs_FK_set(EntityState oldEntitySta
}
}

[Fact] // Issue #6067
[Fact(Skip = "TaskList#19")] // Issue #6067
public void Collection_nav_props_remain_fixed_up_after_manual_fixup_and_DetectChanges()
{
using (var context = new FixupContext())
Expand Down Expand Up @@ -2988,7 +2988,7 @@ private void AssertFixup(DbContext context, Action asserts)
asserts();
}

[Fact] // Issue #4853
[Fact(Skip = "TaskList#19")] // Issue #4853
public void Collection_nav_props_remain_fixed_up_after_DetectChanges()
{
using (var db = new Context4853())
Expand Down
Loading

0 comments on commit 4ec63f8

Please sign in to comment.