Skip to content

Commit

Permalink
Add virtual methods to abstract base classes that implement interface…
Browse files Browse the repository at this point in the history
…s using default member implementations (#28542)
  • Loading branch information
ajcvickers authored Jul 30, 2022
1 parent 183ed87 commit 7f79784
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public ModelCodeGeneratorSelector(IEnumerable<IModelCodeGenerator> services)

/// <inheritdoc />
public virtual IModelCodeGenerator Select(ModelCodeGenerationOptions options)
=> _templatedModelGenerators
.Where(g => options.ProjectDir != null && g.HasTemplates(options.ProjectDir))
.LastOrDefault()
=> _templatedModelGenerators.LastOrDefault(g => options.ProjectDir != null && g.HasTemplates(options.ProjectDir))
?? Select(options.Language);
}
15 changes: 15 additions & 0 deletions src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ public virtual void RemoveAnnotationsHandledByConventions(
ISequence sequence, IDictionary<string, IAnnotation> annotations)
=> RemoveConventionalAnnotationsHelper(sequence, annotations, IsHandledByConvention);

/// <inheritdoc />
public virtual void RemoveAnnotationsHandledByConventions(IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> ((IAnnotationCodeGenerator)this).RemoveAnnotationsHandledByConventionsInternal(annotatable, annotations);

/// <inheritdoc />
public virtual IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
IModel model,
Expand Down Expand Up @@ -504,6 +508,11 @@ public virtual IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
return methodCallCodeFragments;
}

/// <inheritdoc />
public virtual IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> ((IAnnotationCodeGenerator)this).GenerateFluentApiCallsInternal(annotatable, annotations);

/// <inheritdoc />
public virtual IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
IEntityType entityType,
Expand Down Expand Up @@ -538,6 +547,12 @@ public virtual IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttrib
return attributeCodeFragments;
}

/// <inheritdoc />
public virtual IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
IAnnotatable annotatable,
IDictionary<string, IAnnotation> annotations)
=> ((IAnnotationCodeGenerator)this).GenerateDataAnnotationAttributesInternal(annotatable, annotations);

/// <summary>
/// Checks if the given <paramref name="annotation" /> is handled by convention when
/// applied to the given <paramref name="model" />.
Expand Down
17 changes: 16 additions & 1 deletion src/EFCore.Relational/Design/IAnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ void RemoveAnnotationsHandledByConventions(ISequence sequence, IDictionary<strin
/// <param name="annotatable">The annotatable to which the annotations are applied.</param>
/// <param name="annotations">The set of annotations from which to generate fluent API calls.</param>
void RemoveAnnotationsHandledByConventions(IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> RemoveAnnotationsHandledByConventionsInternal(annotatable, annotations);

// Issue #28537.
internal sealed void RemoveAnnotationsHandledByConventionsInternal(
IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
{
switch (annotatable)
{
Expand All @@ -159,7 +164,7 @@ void RemoveAnnotationsHandledByConventions(IAnnotatable annotatable, IDictionary
case IEntityTypeMappingFragment fragment:
RemoveAnnotationsHandledByConventions(fragment, annotations);
return;

case IProperty property:
RemoveAnnotationsHandledByConventions(property, annotations);
return;
Expand Down Expand Up @@ -355,6 +360,11 @@ IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
/// <param name="annotatable">The annotatable to which the annotations are applied.</param>
/// <param name="annotations">The set of annotations from which to generate fluent API calls.</param>
IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> GenerateFluentApiCallsInternal(annotatable, annotations);

// Issue #28537.
internal sealed IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCallsInternal(
IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> annotatable switch
{
IModel model => GenerateFluentApiCalls(model, annotations),
Expand Down Expand Up @@ -405,6 +415,11 @@ IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
IAnnotatable annotatable,
IDictionary<string, IAnnotation> annotations)
=> GenerateDataAnnotationAttributesInternal(annotatable, annotations);

// Issue #28537.
internal sealed IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributesInternal(
IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> annotatable switch
{
IEntityType entityType => GenerateDataAnnotationAttributes(entityType, annotations),
Expand Down
12 changes: 12 additions & 0 deletions src/EFCore.Relational/Diagnostics/DbCommandInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public virtual InterceptionResult<DbCommand> CommandCreating(CommandCorrelatedEv
public virtual DbCommand CommandCreated(CommandEndEventData eventData, DbCommand result)
=> result;

/// <inheritdoc />
public virtual DbCommand CommandInitialized(CommandEndEventData eventData, DbCommand result)
=> result;

/// <inheritdoc />
public virtual InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
Expand Down Expand Up @@ -122,6 +126,14 @@ public virtual Task CommandFailedAsync(
CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <inheritdoc />
public virtual InterceptionResult DataReaderClosing(DbCommand command, DataReaderClosingEventData eventData, InterceptionResult result)
=> result;

/// <inheritdoc />
public virtual ValueTask<InterceptionResult> DataReaderClosingAsync(DbCommand command, DataReaderClosingEventData eventData, InterceptionResult result)
=> new(result);

/// <inheritdoc />
public virtual InterceptionResult DataReaderDisposing(
DbCommand command,
Expand Down
30 changes: 30 additions & 0 deletions src/EFCore.Relational/Diagnostics/DbConnectionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics;
/// </remarks>
public abstract class DbConnectionInterceptor : IDbConnectionInterceptor
{
/// <inheritdoc />
public virtual InterceptionResult<DbConnection> ConnectionCreating(
ConnectionCreatingEventData eventData,
InterceptionResult<DbConnection> result)
=> result;

/// <inheritdoc />
public virtual DbConnection ConnectionCreated(ConnectionCreatedEventData eventData, DbConnection result)
=> result;

/// <inheritdoc />
public virtual InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
=> result;
Expand Down Expand Up @@ -56,6 +66,26 @@ public virtual void ConnectionClosed(DbConnection connection, ConnectionEndEvent
public virtual Task ConnectionClosedAsync(DbConnection connection, ConnectionEndEventData eventData)
=> Task.CompletedTask;

/// <inheritdoc />
public virtual InterceptionResult ConnectionDisposing(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
=> result;

/// <inheritdoc />
public virtual ValueTask<InterceptionResult> ConnectionDisposingAsync(
DbConnection connection,
ConnectionEventData eventData,
InterceptionResult result)
=> new(result);

/// <inheritdoc />
public virtual void ConnectionDisposed(DbConnection connection, ConnectionEndEventData eventData)
{
}

/// <inheritdoc />
public virtual Task ConnectionDisposedAsync(DbConnection connection, ConnectionEndEventData eventData)
=> Task.CompletedTask;

/// <inheritdoc />
public virtual void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventData)
{
Expand Down
5 changes: 2 additions & 3 deletions src/EFCore.Relational/Diagnostics/DbTransactionInterceptor.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Data;

namespace Microsoft.EntityFrameworkCore.Diagnostics;

/// <summary>
Expand Down Expand Up @@ -194,7 +192,8 @@ public virtual void TransactionFailed(DbTransaction transaction, TransactionErro
}

/// <inheritdoc />
public virtual Task TransactionFailedAsync(DbTransaction transaction,
public virtual Task TransactionFailedAsync(
DbTransaction transaction,
TransactionErrorEventData eventData,
CancellationToken cancellationToken = default)
=> Task.CompletedTask;
Expand Down
18 changes: 9 additions & 9 deletions src/EFCore.Relational/Metadata/IColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ bool IsRowVersion
/// Gets the column order.
/// </summary>
/// <value> The column order. </value>
public virtual int? Order
int? Order
=> PropertyMappings.First().Property.GetColumnOrder(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Returns the object that is used as the default value for this column.
/// </summary>
public virtual object? DefaultValue
object? DefaultValue
{
get
{
Expand All @@ -88,7 +88,7 @@ public virtual object? DefaultValue
/// </summary>
/// <param name="defaultValue">The default value.</param>
/// <returns>True if the default value was explicitly set; false otherwise.</returns>
public virtual bool TryGetDefaultValue(out object? defaultValue)
bool TryGetDefaultValue(out object? defaultValue)
{
foreach (var mapping in PropertyMappings)
{
Expand All @@ -114,44 +114,44 @@ public virtual bool TryGetDefaultValue(out object? defaultValue)
/// <summary>
/// Returns the SQL expression that is used as the default value for this column.
/// </summary>
public virtual string? DefaultValueSql
string? DefaultValueSql
=> PropertyMappings.First().Property
.GetDefaultValueSql(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Returns the SQL expression that is used as the computed value for this column.
/// </summary>
public virtual string? ComputedColumnSql
string? ComputedColumnSql
=> PropertyMappings.First().Property
.GetComputedColumnSql(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Returns whether the value of the computed column this property is mapped to is stored in the database, or calculated when
/// it is read.
/// </summary>
public virtual bool? IsStored
bool? IsStored
=> PropertyMappings.First().Property
.GetIsStored(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Comment for this column
/// </summary>
public virtual string? Comment
string? Comment
=> PropertyMappings.First().Property
.GetComment(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Collation for this column
/// </summary>
public virtual string? Collation
string? Collation
=> PropertyMappings.First().Property
.GetCollation(StoreObjectIdentifier.Table(Table.Name, Table.Schema));

/// <summary>
/// Gets the <see cref="ValueComparer" /> for this column.
/// </summary>
/// <returns>The comparer.</returns>
public virtual ValueComparer ProviderValueComparer
ValueComparer ProviderValueComparer
=> PropertyMappings.First().Property
.GetProviderValueComparer();

Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/Metadata/IColumnBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public interface IColumnBase : IAnnotatable
/// </summary>
/// <param name="entityType">An entity type.</param>
/// <returns>The property mapping or <see langword="null" /> if not found.</returns>
public virtual IColumnMappingBase? FindColumnMapping(IReadOnlyEntityType entityType)
IColumnMappingBase? FindColumnMapping(IReadOnlyEntityType entityType)
{
for (var i = 0; i < PropertyMappings.Count; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ MethodCallCodeFragment GenerateUseProvider(
/// <param name="connectionString">The connection string to include in the code fragment.</param>
/// <returns>The code fragment.</returns>
MethodCallCodeFragment GenerateUseProvider(string connectionString)
=> GenerateUseProviderInternal(connectionString);

// Issue #28537.
internal sealed MethodCallCodeFragment GenerateUseProviderInternal(string connectionString)
{
var useProviderCall = GenerateUseProvider(
connectionString,
Expand Down
4 changes: 4 additions & 0 deletions src/EFCore.Relational/Scaffolding/ProviderCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,8 @@ public abstract MethodCallCodeFragment GenerateUseProvider(

return contextOptions;
}

/// <inheritdoc />
public virtual MethodCallCodeFragment GenerateUseProvider(string connectionString)
=> ((IProviderConfigurationCodeGenerator)this).GenerateUseProviderInternal(connectionString);
}
21 changes: 21 additions & 0 deletions src/EFCore.Relational/Update/UpdateSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ public virtual ResultSetMapping AppendInsertOperation(
out bool requiresTransaction)
=> AppendInsertReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);

/// <inheritdoc />
public virtual ResultSetMapping AppendInsertOperation(
StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
int commandPosition)
=> AppendInsertOperation(commandStringBuilder, command, commandPosition, out _);

/// <summary>
/// Appends SQL for inserting a row to the commands being built, via an INSERT containing an optional RETURNING clause to retrieve
/// any database-generated values.
Expand Down Expand Up @@ -106,6 +113,13 @@ public virtual ResultSetMapping AppendUpdateOperation(
out bool requiresTransaction)
=> AppendUpdateReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);

/// <inheritdoc />
public virtual ResultSetMapping AppendUpdateOperation(
StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
int commandPosition)
=> AppendUpdateOperation(commandStringBuilder, command, commandPosition, out _);

/// <summary>
/// Appends SQL for updating a row to the commands being built, via an UPDATE containing an RETURNING clause to retrieve any
/// database-generated values or for concurrency checking.
Expand Down Expand Up @@ -153,6 +167,13 @@ public virtual ResultSetMapping AppendDeleteOperation(
out bool requiresTransaction)
=> AppendDeleteReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);

/// <inheritdoc />
public virtual ResultSetMapping AppendDeleteOperation(
StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
int commandPosition)
=> AppendDeleteOperation(commandStringBuilder, command, commandPosition, out _);

/// <summary>
/// Appends SQL for deleting a row to the commands being built, via a DELETE containing a RETURNING clause for concurrency checking.
/// </summary>
Expand Down
29 changes: 18 additions & 11 deletions src/EFCore/Diagnostics/SaveChangesInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ public virtual void SaveChangesFailed(DbContextErrorEventData eventData)
{
}

/// <inheritdoc />
public virtual void SaveChangesCanceled(DbContextEventData eventData)
{
}

/// <inheritdoc />
public virtual ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
Expand All @@ -45,14 +40,26 @@ public virtual ValueTask<int> SavedChangesAsync(
=> new(result);

/// <inheritdoc />
public virtual Task SaveChangesFailedAsync(
DbContextErrorEventData eventData,
CancellationToken cancellationToken = default)
public virtual Task SaveChangesFailedAsync(DbContextErrorEventData eventData, CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <inheritdoc />
public virtual Task SaveChangesCanceledAsync(
DbContextEventData eventData,
CancellationToken cancellationToken = default)
public virtual void SaveChangesCanceled(DbContextEventData eventData)
{
}

/// <inheritdoc />
public virtual Task SaveChangesCanceledAsync(DbContextEventData eventData, CancellationToken cancellationToken = default)
=> Task.CompletedTask;

/// <inheritdoc />
public virtual InterceptionResult ThrowingConcurrencyException(ConcurrencyExceptionEventData eventData, InterceptionResult result)
=> result;

/// <inheritdoc />
public virtual ValueTask<InterceptionResult> ThrowingConcurrencyExceptionAsync(
ConcurrencyExceptionEventData eventData,
InterceptionResult result,
CancellationToken cancellationToken = default)
=> new(result);
}
9 changes: 9 additions & 0 deletions src/EFCore/Internal/DbContextFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,13 @@ public DbContextFactory(
/// </summary>
public virtual TContext CreateDbContext()
=> _factory(_serviceProvider, _options);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 Task<TContext> CreateDbContextAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(CreateDbContext());
}
Loading

0 comments on commit 7f79784

Please sign in to comment.