Skip to content

Commit

Permalink
Query: Don't track materialized keyless entity instances
Browse files Browse the repository at this point in the history
Rather than requiring AsNoTracking in the query.

Resolves #17102
  • Loading branch information
smitpatel committed Aug 12, 2019
1 parent 669ea67 commit 16e314e
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 38 deletions.
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

0 comments on commit 16e314e

Please sign in to comment.