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

API review bundle #28504

Merged
merged 1 commit into from
Jul 26, 2022
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
15 changes: 15 additions & 0 deletions src/EFCore.Abstractions/IndexAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,25 @@ public sealed class IndexAttribute : Attribute
private bool? _isUnique;
private bool[]? _isDescending;

/// <summary>
/// Initializes a new instance of the <see cref="IndexAttribute" /> class.
/// </summary>
/// <param name="propertyName">The first (or only) property in the index.</param>
/// <param name="additionalPropertyNames">The additional properties which constitute the index, if any, in order.</param>
public IndexAttribute(string propertyName, params string[] additionalPropertyNames)
{
Check.NotEmpty(propertyName, nameof(propertyName));
Check.HasNoEmptyElements(additionalPropertyNames, nameof(additionalPropertyNames));

PropertyNames = new List<string> { propertyName };
((List<string>)PropertyNames).AddRange(additionalPropertyNames);
}

/// <summary>
/// Initializes a new instance of the <see cref="IndexAttribute" /> class.
/// </summary>
/// <param name="propertyNames">The properties which constitute the index, in order (there must be at least one).</param>
[Obsolete("Use the other constructor")]
public IndexAttribute(params string[] propertyNames)
{
Check.NotEmpty(propertyNames, nameof(propertyNames));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public ProxyBindingInterceptor(IProxyFactory proxyFactory)
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual InstantiationBinding ModifyBinding(IEntityType entityType, string entityInstanceName, InstantiationBinding binding)
public virtual InstantiationBinding ModifyBinding(InstantiationBindingInterceptionData interceptionData, InstantiationBinding binding)
{
var entityType = interceptionData.EntityType;
var proxyType = _proxyFactory.CreateProxyType(entityType);

if ((bool?)entityType.Model[ProxyAnnotationNames.LazyLoading] == true)
Expand Down
3 changes: 2 additions & 1 deletion src/EFCore/ChangeTracking/Internal/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -781,14 +781,15 @@ public virtual bool ResolveToExistingEntry(
{
if (_resolutionInterceptor != null)
{
var interceptionData = new IdentityResolutionInterceptionData(Context);
var needsTracking = false;
foreach (var key in newEntry.EntityType.GetKeys())
{
var existingEntry = FindIdentityMap(key)?.TryGetEntry(newEntry);
if (existingEntry != null)
{
_resolutionInterceptor.UpdateTrackedInstance(
Context,
interceptionData,
new EntityEntry(existingEntry),
newEntry.Entity);

Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Diagnostics/CoreLoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ public static (Expression Query, QueryExpressionEventData? EventData) QueryCompi

if (interceptor != null)
{
return (interceptor.ProcessingQuery(queryExpression, eventData), eventData);
return (interceptor.QueryCompilationStarting(queryExpression, eventData), eventData);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/EFCore/Diagnostics/IIdentityResolutionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public interface IIdentityResolutionInterceptor : IInterceptor
/// an already tracked instance. This method must apply any property values and relationship changes from the new instance
/// into the existing instance. The new instance is then discarded.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> is use.</param>
/// <param name="interceptionData">Contextual information about the identity resolution.</param>
/// <param name="existingEntry">The entry for the existing tracked entity instance.</param>
/// <param name="newInstance">The new entity instance, which will be discarded after this call.</param>
void UpdateTrackedInstance(DbContext context, EntityEntry existingEntry, object newInstance);
/// <param name="newEntity">The new entity instance, which will be discarded after this call.</param>
void UpdateTrackedInstance(IdentityResolutionInterceptionData interceptionData, EntityEntry existingEntry, object newEntity);
}
5 changes: 2 additions & 3 deletions src/EFCore/Diagnostics/IInstantiationBindingInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ public interface IInstantiationBindingInterceptor : ISingletonInterceptor
/// <summary>
/// Returns a new <see cref="InstantiationBinding" /> for the given entity type, potentially modified from the given binding.
/// </summary>
/// <param name="entityType">The entity type for which the binding is being used.</param>
/// <param name="entityInstanceName">The name of the instance being materialized.</param>
/// <param name="interceptionData">Contextual information about the binding.</param>
/// <param name="binding">The current binding.</param>
/// <returns>A new binding.</returns>
InstantiationBinding ModifyBinding(IEntityType entityType, string entityInstanceName, InstantiationBinding binding);
InstantiationBinding ModifyBinding(InstantiationBindingInterceptionData interceptionData, InstantiationBinding binding);
}
20 changes: 10 additions & 10 deletions src/EFCore/Diagnostics/IMaterializationInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,24 @@ InterceptionResult<object> CreatingInstance(MaterializationInterceptionData mate
/// any properties values not set by the constructor have been set.
/// </summary>
/// <param name="materializationData">Contextual information about the materialization happening.</param>
/// <param name="instance">
/// <param name="entity">
/// The entity instance that has been created.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// The entity instance that EF will use.
/// An implementation of this method for any interceptor that is not attempting to change the instance used
/// must return the <paramref name="instance" /> value passed in.
/// must return the <paramref name="entity" /> value passed in.
/// </returns>
object CreatedInstance(MaterializationInterceptionData materializationData, object instance)
=> instance;
object CreatedInstance(MaterializationInterceptionData materializationData, object entity)
=> entity;

/// <summary>
/// Called immediately before EF is going to set property values of an entity that has just been created. Note that property values
/// set by the constructor will already have been set.
/// </summary>
/// <param name="materializationData">Contextual information about the materialization happening.</param>
/// <param name="instance">The entity instance for which property values will be set.</param>
/// <param name="entity">The entity instance for which property values will be set.</param>
/// <param name="result">
/// Represents the current result if one exists.
/// This value will have <see cref="InterceptionResult.IsSuppressed" /> set to <see langword="true" /> if some previous
Expand All @@ -67,22 +67,22 @@ object CreatedInstance(MaterializationInterceptionData materializationData, obje
/// An implementation of this method for any interceptor that is not attempting to suppress
/// setting property values must return the <paramref name="result" /> value passed in.
/// </returns>
InterceptionResult InitializingInstance(MaterializationInterceptionData materializationData, object instance, InterceptionResult result)
InterceptionResult InitializingInstance(MaterializationInterceptionData materializationData, object entity, InterceptionResult result)
=> result;

/// <summary>
/// Called immediately after EF has set property values of an entity that has just been created.
/// </summary>
/// <param name="materializationData">Contextual information about the materialization happening.</param>
/// <param name="instance">
/// <param name="entity">
/// The entity instance that has been created.
/// This value is typically used as the return value for the implementation of this method.
/// </param>
/// <returns>
/// The entity instance that EF will use.
/// An implementation of this method for any interceptor that is not attempting to change the instance used
/// must return the <paramref name="instance" /> value passed in.
/// must return the <paramref name="entity" /> value passed in.
/// </returns>
object InitializedInstance(MaterializationInterceptionData materializationData, object instance)
=> instance;
object InitializedInstance(MaterializationInterceptionData materializationData, object entity)
=> entity;
}
30 changes: 1 addition & 29 deletions src/EFCore/Diagnostics/IQueryExpressionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,8 @@ public interface IQueryExpressionInterceptor : IInterceptor
/// <param name="queryExpression">The query expression.</param>
/// <param name="eventData">Contextual information about the query environment.</param>
/// <returns>The query expression tree to continue with, which may have been changed by the interceptor.</returns>
Expression ProcessingQuery(
Expression QueryCompilationStarting(
Expression queryExpression,
QueryExpressionEventData eventData)
=> queryExpression;

/// <summary>
/// Called when EF is about to compile the query delegate that will be used to execute the query.
/// </summary>
/// <param name="queryExpression">The query expression.</param>
/// <param name="queryExecutorExpression">The expression that will be compiled into the execution delegate.</param>
/// <param name="eventData">Contextual information about the query environment.</param>
/// <typeparam name="TResult">The return type of the execution delegate.</typeparam>
/// <returns>The expression that will be compiled into the execution delegate, which may have been changed by the interceptor.</returns>
Expression<Func<QueryContext, TResult>> CompilingQuery<TResult>(
Expression queryExpression,
Expression<Func<QueryContext, TResult>> queryExecutorExpression,
QueryExpressionEventData eventData)
=> queryExecutorExpression;

/// <summary>
/// Called when EF is about to compile the query delegate that will be used to execute the query.
/// </summary>
/// <param name="queryExpression">The query expression.</param>
/// <param name="eventData">Contextual information about the query environment.</param>
/// <param name="queryExecutor">The delegate that will be used to execute the query.</param>
/// <typeparam name="TResult">The return type of the execution delegate.</typeparam>
/// <returns>The delegate that will be used to execute the query, which may have been changed by the interceptor.</returns>
Func<QueryContext, TResult> CompiledQuery<TResult>(
Expression queryExpression,
QueryExpressionEventData eventData,
Func<QueryContext, TResult> queryExecutor)
=> queryExecutor;
}
27 changes: 27 additions & 0 deletions src/EFCore/Diagnostics/IdentityResolutionInterceptionData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Diagnostics;

/// <summary>
/// A parameter object passed to <see cref="IIdentityResolutionInterceptor" /> methods.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-diagnostics">Logging, events, and diagnostics</see> for more information and examples.
/// </remarks>
public readonly struct IdentityResolutionInterceptionData
{
/// <summary>
/// Constructs the parameter object.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> in use.</param>
public IdentityResolutionInterceptionData(DbContext context)
{
Context = context;
}

/// <summary>
/// The current <see cref="DbContext" /> instance being used.
/// </summary>
public DbContext Context { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics;
/// A <see cref="IIdentityResolutionInterceptor"/> that ignores the new instance and retains property values from the existing
/// tracked instance.
/// </summary>
public class SkippingIdentityResolutionInterceptor : IIdentityResolutionInterceptor
public class IgnoringIdentityResolutionInterceptor : IIdentityResolutionInterceptor
{
/// <summary>
/// Called when a <see cref="DbContext"/> attempts to track a new instance of an entity with the same primary key value as
/// an already tracked instance. This implementation does nothing, such that property values from the existing tracked
/// instance are retained.
/// </summary>
/// <param name="context">The <see cref="DbContext"/> is use.</param>
/// <param name="interceptionData">Contextual information about the identity resolution.</param>
/// <param name="existingEntry">The entry for the existing tracked entity instance.</param>
/// <param name="newInstance">The new entity instance, which will be discarded after this call.</param>
public virtual void UpdateTrackedInstance(DbContext context, EntityEntry existingEntry, object newInstance)
/// <param name="newEntity">The new entity instance, which will be discarded after this call.</param>
public virtual void UpdateTrackedInstance(IdentityResolutionInterceptionData interceptionData, EntityEntry existingEntry, object newEntity)
{
}
}
28 changes: 28 additions & 0 deletions src/EFCore/Diagnostics/InstantiationBindingInterceptionData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Diagnostics;

/// <summary>
/// A parameter object passed to <see cref="IInstantiationBindingInterceptor" /> methods.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-diagnostics">Logging, events, and diagnostics</see> for more information and examples.
/// </remarks>
public readonly struct InstantiationBindingInterceptionData
{

/// <summary>
/// Constructs the parameter object.
/// </summary>
/// <param name="entityType">The entity type for which the binding is being used.</param>
public InstantiationBindingInterceptionData(IEntityType entityType)
{
EntityType = entityType;
}

/// <summary>
/// The entity type for which the binding is being used.
/// </summary>
public IEntityType EntityType { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ public CompositeIdentityResolutionInterceptor(IEnumerable<IIdentityResolutionInt
_interceptors = interceptors.ToArray();
}

public void UpdateTrackedInstance(DbContext context, EntityEntry existingEntry, object newInstance)
public void UpdateTrackedInstance(IdentityResolutionInterceptionData interceptionData, EntityEntry existingEntry, object newEntity)
{
for (var i = 0; i < _interceptors.Length; i++)
{
_interceptors[i].UpdateTrackedInstance(context, existingEntry, newInstance);
_interceptors[i].UpdateTrackedInstance(interceptionData, existingEntry, newEntity);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,39 +42,39 @@ public InterceptionResult<object> CreatingInstance(

public object CreatedInstance(
MaterializationInterceptionData materializationData,
object instance)
object entity)
{
for (var i = 0; i < _interceptors.Length; i++)
{
instance = _interceptors[i].CreatedInstance(materializationData, instance);
entity = _interceptors[i].CreatedInstance(materializationData, entity);
}

return instance;
return entity;
}

public InterceptionResult InitializingInstance(
MaterializationInterceptionData materializationData,
object instance,
object entity,
InterceptionResult result)
{
for (var i = 0; i < _interceptors.Length; i++)
{
result = _interceptors[i].InitializingInstance(materializationData, instance, result);
result = _interceptors[i].InitializingInstance(materializationData, entity, result);
}

return result;
}

public object InitializedInstance(
MaterializationInterceptionData materializationData,
object instance)
object entity)
{
for (var i = 0; i < _interceptors.Length; i++)
{
instance = _interceptors[i].InitializedInstance(materializationData, instance);
entity = _interceptors[i].InitializedInstance(materializationData, entity);
}

return instance;
return entity;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,16 @@ public CompositeQueryExpressionInterceptor(IEnumerable<IQueryExpressionIntercept
_interceptors = interceptors.ToArray();
}

public Expression ProcessingQuery(
public Expression QueryCompilationStarting(
Expression queryExpression,
QueryExpressionEventData eventData)
{
for (var i = 0; i < _interceptors.Length; i++)
{
queryExpression = _interceptors[i].ProcessingQuery(queryExpression, eventData);
queryExpression = _interceptors[i].QueryCompilationStarting(queryExpression, eventData);
}

return queryExpression;
}

public Expression<Func<QueryContext, TResult>> CompilingQuery<TResult>(
Expression queryExpression,
Expression<Func<QueryContext, TResult>> queryExecutorExpression,
QueryExpressionEventData eventData)
{
for (var i = 0; i < _interceptors.Length; i++)
{
queryExecutorExpression = _interceptors[i].CompilingQuery(queryExpression, queryExecutorExpression, eventData);
}

return queryExecutorExpression;
}

public Func<QueryContext, TResult> CompiledQuery<TResult>(
Expression queryExpression,
QueryExpressionEventData eventData,
Func<QueryContext, TResult> queryExecutor)
{
for (var i = 0; i < _interceptors.Length; i++)
{
queryExecutor = _interceptors[i].CompiledQuery(queryExpression, eventData, queryExecutor);
}

return queryExecutor;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ public UpdatingIdentityResolutionInterceptor(
/// an already tracked instance. This implementation copies property values from the new entity instance into the
/// tracked entity instance.
/// </summary>
/// <param name="context">The <see cref="DbContext" /> is use.</param>
/// <param name="interceptionData">Contextual information about the identity resolution.</param>
/// <param name="existingEntry">The entry for the existing tracked entity instance.</param>
/// <param name="newInstance">The new entity instance, which will be discarded after this call.</param>
public virtual void UpdateTrackedInstance(DbContext context, EntityEntry existingEntry, object newInstance)
/// <param name="newEntity">The new entity instance, which will be discarded after this call.</param>
public virtual void UpdateTrackedInstance(IdentityResolutionInterceptionData interceptionData, EntityEntry existingEntry, object newEntity)
{
var tempEntry = context.Entry(newInstance);
var tempEntry = interceptionData.Context.Entry(newEntity);

if (existingEntry.State == EntityState.Added)
{
Expand Down
Loading