Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Don't track materialized keyless entity instances #17108

Merged
merged 1 commit into from
Aug 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ private static void IncludeReference<TEntity, TIncludingEntity, TIncludedEntity>
{
if (entity is TIncludingEntity includingEntity)
{
if (trackingQuery)
if (trackingQuery
&& navigation.DeclaringEntityType.FindPrimaryKey() != null)
{
// For non-null relatedEntity StateManager will set the flag
if (relatedEntity == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ private static void IncludeReference<TEntity, TIncludingEntity, TIncludedEntity>
{
if (entity is TIncludingEntity includingEntity)
{
if (trackingQuery)
if (trackingQuery
&& navigation.DeclaringEntityType.FindPrimaryKey() != null)
{
// For non-null relatedEntity StateManager will set the flag
if (relatedEntity == null)
Expand Down
13 changes: 1 addition & 12 deletions src/EFCore/Internal/InternalDbSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,7 @@ private EntityQueryable<TEntity> EntityQueryable
}

private EntityQueryable<TEntity> CreateEntityQueryable()
{
var queryable = new EntityQueryable<TEntity>(_context.GetDependencies().QueryProvider);

#pragma warning disable 618
if (_entityType.FindPrimaryKey() == null)
#pragma warning restore 618
{
queryable = (EntityQueryable<TEntity>)queryable.AsNoTracking();
}

return queryable;
}
=> new EntityQueryable<TEntity>(_context.GetDependencies().QueryProvider);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
12 changes: 4 additions & 8 deletions src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ protected ShapedQueryCompilingExpressionVisitor(
QueryCompilationContext queryCompilationContext)
{
Dependencies = dependencies;

IsTracking = queryCompilationContext.IsTracking;

_entityMaterializerInjectingExpressionVisitor =
Expand Down Expand Up @@ -283,11 +282,6 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres

var primaryKey = entityType.FindPrimaryKey();

if (_trackQueryResults && primaryKey == null)
{
throw new InvalidOperationException("A tracking query contains entityType without key in final result.");
}

var concreteEntityTypeVariable = Expression.Variable(typeof(IEntityType),
"entityType" + _currentEntityIndex);
variables.Add(concreteEntityTypeVariable);
Expand All @@ -298,7 +292,8 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres
instanceVariable,
Expression.Constant(null, entityType.ClrType)));

if (_trackQueryResults)
if (_trackQueryResults
&& primaryKey != null)
{
var entryVariable = Expression.Variable(typeof(InternalEntityEntry), "entry" + _currentEntityIndex);
var hasNullKeyVariable = Expression.Variable(typeof(bool), "hasNullKey" + _currentEntityIndex);
Expand Down Expand Up @@ -439,7 +434,8 @@ var discriminatorValue

expressions.Add(Expression.Assign(instanceVariable, materializationExpression));

if (_trackQueryResults)
if (_trackQueryResults
&& entityType.FindPrimaryKey() != null)
{
_visitedEntityTypes.Add(entityType);

Expand Down
9 changes: 6 additions & 3 deletions test/EFCore.Specification.Tests/LazyLoadProxyTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -756,12 +756,15 @@ public virtual void Lazy_load_one_to_one_reference_to_dependent_not_found(Entity
}

[ConditionalTheory]
[InlineData(EntityState.Unchanged, CascadeTiming.OnSaveChanges)]
[InlineData(EntityState.Modified, CascadeTiming.OnSaveChanges)]
[InlineData(EntityState.Deleted, CascadeTiming.OnSaveChanges)]
[InlineData(EntityState.Unchanged, CascadeTiming.Immediate)]
[InlineData(EntityState.Modified, CascadeTiming.Immediate)]
[InlineData(EntityState.Deleted, CascadeTiming.Immediate)]
[InlineData(EntityState.Unchanged, CascadeTiming.Immediate)]
[InlineData(EntityState.Modified, CascadeTiming.Immediate)]
[InlineData(EntityState.Deleted, CascadeTiming.Immediate)]
[InlineData(EntityState.Unchanged, CascadeTiming.Never)]
[InlineData(EntityState.Modified, CascadeTiming.Never)]
[InlineData(EntityState.Deleted, CascadeTiming.Never)]
public virtual void Lazy_load_collection_already_loaded(EntityState state, CascadeTiming cascadeDeleteTiming)
{
using (var context = CreateContext(lazyLoadingEnabled: true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public virtual void Can_query_all_animal_views()
{
using (var context = CreateContext())
{
var animalQueries = context.Set<AnimalQuery>().AsNoTracking().OrderBy(av => av.CountryId).ToList();
var animalQueries = context.Set<AnimalQuery>().OrderBy(av => av.CountryId).ToList();

Assert.Equal(2, animalQueries.Count);
Assert.IsType<KiwiQuery>(animalQueries[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public virtual Task KeylessEntity_simple(bool isAsync)
{
return AssertQuery<CustomerView>(
isAsync,
cvs => cvs.AsNoTracking());
cvs => cvs);
}

[ConditionalTheory]
Expand All @@ -31,15 +31,15 @@ public virtual Task KeylessEntity_where_simple(bool isAsync)
{
return AssertQuery<CustomerView>(
isAsync,
cvs => cvs.AsNoTracking().Where(c => c.City == "London"));
cvs => cvs.Where(c => c.City == "London"));
}

[ConditionalFact]
public virtual void KeylessEntity_by_database_view()
{
using (var context = CreateContext())
{
var results = context.Set<ProductQuery>().AsNoTracking().ToArray();
var results = context.Set<ProductQuery>().ToArray();

Assert.Equal(69, results.Length);
}
Expand All @@ -50,7 +50,7 @@ public virtual void Auto_initialized_view_set()
{
using (var context = CreateContext())
{
var results = context.CustomerQueries.AsNoTracking().ToArray();
var results = context.CustomerQueries.ToArray();

Assert.Equal(91, results.Length);
}
Expand All @@ -62,7 +62,7 @@ public virtual void KeylessEntity_with_nav_defining_query()
using (var context = CreateContext())
{
var results
= context.Set<CustomerQuery>().AsNoTracking()
= context.Set<CustomerQuery>()
.Where(cq => cq.OrderCount > 0)
.ToArray();

Expand All @@ -76,7 +76,7 @@ public virtual Task KeylessEntity_with_defining_query(bool isAsync)
{
return AssertQuery<OrderQuery>(
isAsync,
ovs => ovs.AsNoTracking().Where(ov => ov.CustomerID == "ALFKI"));
ovs => ovs.Where(ov => ov.CustomerID == "ALFKI"));
}

// also issue 12873
Expand All @@ -86,7 +86,7 @@ public virtual Task KeylessEntity_with_defining_query_and_correlated_collection(
{
return AssertQuery<OrderQuery>(
isAsync,
ovs => ovs.AsNoTracking().Where(ov => ov.CustomerID == "ALFKI").Select(ov => ov.Customer)
ovs => ovs.Where(ov => ov.CustomerID == "ALFKI").Select(ov => ov.Customer)
.Select(cv => cv.Orders.Where(cc => true).ToList()));
}

Expand All @@ -98,7 +98,7 @@ public virtual Task KeylessEntity_with_mixed_tracking(bool isAsync)
isAsync,
(cs, ovs)
=> from c in cs
from o in ovs.AsNoTracking().Where(ov => ov.CustomerID == c.CustomerID)
from o in ovs.Where(ov => ov.CustomerID == c.CustomerID)
select new
{
c,
Expand All @@ -113,7 +113,7 @@ public virtual Task KeylessEntity_with_included_nav(bool isAsync)
{
return AssertIncludeQuery<OrderQuery>(
isAsync,
ovs => from ov in ovs.AsNoTracking().Include(ov => ov.Customer)
ovs => from ov in ovs.Include(ov => ov.Customer)
where ov.CustomerID == "ALFKI"
select ov,
new List<IExpectedInclude>
Expand All @@ -128,7 +128,7 @@ public virtual Task KeylessEntity_with_included_navs_multi_level(bool isAsync)
{
return AssertIncludeQuery<OrderQuery>(
isAsync,
ovs => from ov in ovs.AsNoTracking().Include(ov => ov.Customer.Orders)
ovs => from ov in ovs.Include(ov => ov.Customer.Orders)
where ov.CustomerID == "ALFKI"
select ov,
new List<IExpectedInclude>
Expand All @@ -144,7 +144,7 @@ public virtual Task KeylessEntity_select_where_navigation(bool isAsync)
{
return AssertQuery<OrderQuery>(
isAsync,
ovs => from ov in ovs.AsNoTracking()
ovs => from ov in ovs
where ov.Customer.City == "Seattle"
select ov);
}
Expand All @@ -155,7 +155,7 @@ public virtual Task KeylessEntity_select_where_navigation_multi_level(bool isAsy
{
return AssertQuery<OrderQuery>(
isAsync,
ovs => from ov in ovs.AsNoTracking()
ovs => from ov in ovs
where ov.Customer.Orders.Any()
select ov);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4874,8 +4874,6 @@ FROM [Orders] AS [o]
WHERE (([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL) AND (([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL)) > 0");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public override async Task Convert_to_nullable_on_nullable_value_is_ignored(bool isAsync)
{
await base.Convert_to_nullable_on_nullable_value_is_ignored(isAsync);
Expand Down