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

Fixes #28516
  • Loading branch information
ajcvickers committed Jul 29, 2022
1 parent 1ff156c commit 78ba70e
Show file tree
Hide file tree
Showing 15 changed files with 239 additions and 28 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);
}
99 changes: 99 additions & 0 deletions src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,69 @@ public virtual void RemoveAnnotationsHandledByConventions(
ISequence sequence, IDictionary<string, IAnnotation> annotations)
=> RemoveConventionalAnnotationsHelper(sequence, annotations, IsHandledByConvention);

// Issue #28537. Remember to update both the class and the interface implementation.
/// <inheritdoc />
public virtual void RemoveAnnotationsHandledByConventions(IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
{
switch (annotatable)
{
case IModel model:
RemoveAnnotationsHandledByConventions(model, annotations);
return;

case IEntityType entityType:
RemoveAnnotationsHandledByConventions(entityType, annotations);
return;

case IEntityTypeMappingFragment fragment:
RemoveAnnotationsHandledByConventions(fragment, annotations);
return;

case IProperty property:
RemoveAnnotationsHandledByConventions(property, annotations);
return;

case IKey key:
RemoveAnnotationsHandledByConventions(key, annotations);
return;

case IForeignKey foreignKey:
RemoveAnnotationsHandledByConventions(foreignKey, annotations);
return;

case INavigation navigation:
RemoveAnnotationsHandledByConventions(navigation, annotations);
return;

case ISkipNavigation skipNavigation:
RemoveAnnotationsHandledByConventions(skipNavigation, annotations);
return;

case ICheckConstraint checkConstraint:
RemoveAnnotationsHandledByConventions(checkConstraint, annotations);
return;

case IIndex index:
RemoveAnnotationsHandledByConventions(index, annotations);
return;

case ITrigger trigger:
RemoveAnnotationsHandledByConventions(trigger, annotations);
return;

case IRelationalPropertyOverrides overrides:
RemoveAnnotationsHandledByConventions(overrides, annotations);
return;

case ISequence sequence:
RemoveAnnotationsHandledByConventions(sequence, annotations);
return;

default:
throw new ArgumentException(RelationalStrings.UnhandledAnnotatableType(annotatable.GetType()));
}
}

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

// Issue #28537. Remember to update both the class and the interface implementation.
/// <inheritdoc />
public virtual IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> annotatable switch
{
IModel model => GenerateFluentApiCalls(model, annotations),
IEntityType entityType => GenerateFluentApiCalls(entityType, annotations),
IEntityTypeMappingFragment fragment => GenerateFluentApiCalls(fragment, annotations),
IProperty property => GenerateFluentApiCalls(property, annotations),
IRelationalPropertyOverrides overrides => GenerateFluentApiCalls(overrides, annotations),
IKey key => GenerateFluentApiCalls(key, annotations),
IForeignKey foreignKey => GenerateFluentApiCalls(foreignKey, annotations),
INavigation navigation => GenerateFluentApiCalls(navigation, annotations),
ISkipNavigation skipNavigation => GenerateFluentApiCalls(skipNavigation, annotations),
IIndex index => GenerateFluentApiCalls(index, annotations),
ICheckConstraint checkConstraint => GenerateFluentApiCalls(checkConstraint, annotations),
ITrigger trigger => GenerateFluentApiCalls(trigger, annotations),
ISequence sequence => GenerateFluentApiCalls(sequence, annotations),

_ => throw new ArgumentException(RelationalStrings.UnhandledAnnotatableType(annotatable.GetType()))
};

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


// Issue #28537. Remember to update both the class and the interface implementation.
/// <inheritdoc />
public virtual IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
IAnnotatable annotatable,
IDictionary<string, IAnnotation> annotations)
=> annotatable switch
{
IEntityType entityType => GenerateDataAnnotationAttributes(entityType, annotations),
IProperty property => GenerateDataAnnotationAttributes(property, annotations),
_ => throw new ArgumentException(RelationalStrings.UnhandledAnnotatableType(annotatable.GetType()))
};

/// <summary>
/// Checks if the given <paramref name="annotation" /> is handled by convention when
/// applied to the given <paramref name="model" />.
Expand Down
3 changes: 3 additions & 0 deletions src/EFCore.Relational/Design/IAnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ void RemoveAnnotationsHandledByConventions(ISequence sequence, IDictionary<strin
/// </summary>
/// <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>
// Issue #28537. Remember to update both the class and the interface implementation.
void RemoveAnnotationsHandledByConventions(IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
{
switch (annotatable)
Expand Down Expand Up @@ -354,6 +355,7 @@ IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
/// </summary>
/// <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>
// Issue #28537. Remember to update both the class and the interface implementation.
IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(IAnnotatable annotatable, IDictionary<string, IAnnotation> annotations)
=> annotatable switch
{
Expand Down Expand Up @@ -391,6 +393,7 @@ IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
/// </summary>
/// <param name="property">The property to which the annotations are applied.</param>
/// <param name="annotations">The set of annotations from which to generate fluent API calls.</param>
// Issue #28537. Remember to update both the class and the interface implementation.
IReadOnlyList<AttributeCodeFragment> GenerateDataAnnotationAttributes(
IProperty property,
IDictionary<string, IAnnotation> 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 @@ -49,6 +49,7 @@ MethodCallCodeFragment GenerateUseProvider(
/// </summary>
/// <param name="connectionString">The connection string to include in the code fragment.</param>
/// <returns>The code fragment.</returns>
// Issue #28537. Remember to update both the class and the interface implementation.
MethodCallCodeFragment GenerateUseProvider(string connectionString)
{
var useProviderCall = GenerateUseProvider(
Expand Down
16 changes: 16 additions & 0 deletions src/EFCore.Relational/Scaffolding/ProviderCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,20 @@ public abstract MethodCallCodeFragment GenerateUseProvider(

return contextOptions;
}

// Issue #28537. Remember to update both the class and the interface implementation.
/// <inheritdoc />
public virtual MethodCallCodeFragment GenerateUseProvider(string connectionString)
{
var useProviderCall = GenerateUseProvider(
connectionString,
GenerateProviderOptions());
var contextOptions = GenerateContextOptions();
if (contextOptions != null)
{
useProviderCall = useProviderCall.Chain(contextOptions);
}

return useProviderCall;
}
}
3 changes: 3 additions & 0 deletions src/EFCore.Relational/Update/IUpdateSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ ResultSetMapping AppendDeleteOperation(
/// <param name="command">The command that represents the delete operation.</param>
/// <param name="commandPosition">The ordinal of this command in the batch.</param>
/// <returns>The <see cref="ResultSetMapping" /> for the command.</returns>
// Issue #28537. Remember to update both the class and the interface implementation.
ResultSetMapping AppendDeleteOperation(
StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
Expand Down Expand Up @@ -128,6 +129,7 @@ ResultSetMapping AppendInsertOperation(
/// <param name="command">The command that represents the delete operation.</param>
/// <param name="commandPosition">The ordinal of this command in the batch.</param>
/// <returns>The <see cref="ResultSetMapping" /> for the command.</returns>
// Issue #28537. Remember to update both the class and the interface implementation.
ResultSetMapping AppendInsertOperation(
StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
Expand Down Expand Up @@ -155,6 +157,7 @@ ResultSetMapping AppendUpdateOperation(
/// <param name="command">The command that represents the delete operation.</param>
/// <param name="commandPosition">The ordinal of this command in the batch.</param>
/// <returns>The <see cref="ResultSetMapping" /> for the command.</returns>
// Issue #28537. Remember to update both the class and the interface implementation.
ResultSetMapping AppendUpdateOperation(
StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
Expand Down
Loading

0 comments on commit 78ba70e

Please sign in to comment.