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

ADO.NET batching API #54467

Merged
merged 6 commits into from
Jul 4, 2021
Merged

ADO.NET batching API #54467

merged 6 commits into from
Jul 4, 2021

Conversation

roji
Copy link
Member

@roji roji commented Jun 20, 2021

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Jun 20, 2021

Tagging subscribers to this area: @roji, @ajcvickers
See info in area-owners.md if you want to be subscribed.

Issue Details

Closes #28633

/cc @bgrainger @Wraith2 @bgribaudo @FransBouma @GSPP @bricelam @ajcvickers

Author: roji
Assignees: -
Labels:

area-System.Data, new-api-needs-documentation

Milestone: -


protected virtual DbBatchCommand CreateDbBatchCommand() => throw new NotSupportedException();

public virtual void Dispose() {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub can you please confirm that this (and the below DisposeAsync) is acceptable in an abstract base class, given there are no resources to be disposed on it, and that the dispose pattern could simply be implemented by implementations if needed?

Also, I've made this non-abstract since most implementations aren't expected to override.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little surprised to not see the standard dispose pattern implemented here, although I do expect the inheritance hierarchy to be very shallow (a single derived class per provider, and it should be sealed), thus I can't imagine it being a problem in practice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bgrainger missed this originally.

I think the standard dispose pattern makes sense when the base class already have some disposable resource; DbCommand/DbConnection extend Component (DbBatch doesn't), which itself is disposable (mainly for event management, AFAIK), which is why the pattern is followed there. For an empty base class with no disposable resources, I'm not sure what the pattern would bring - it can always be added in implementing classes, if those classes have a disposable resource and expect to be themselves inherited...

Let me know if you see any issue with this logic.

@roji
Copy link
Member Author

roji commented Jun 20, 2021

See some discussion in #28633 (comment)

@roji roji marked this pull request as draft June 21, 2021 16:36
@roji roji marked this pull request as ready for review June 29, 2021 13:20
Comment on lines +1933 to +1934
public System.Threading.Tasks.Task<System.Data.Common.DbDataReader> ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; }
public System.Threading.Tasks.Task<System.Data.Common.DbDataReader> ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) { throw null; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why two overloads instead of two defaulted parameters?

(I thought I remembered a discussion about a different new ADO.NET API where it was decided that new code didn't have to follow the existing pattern of multiple overloads. Or maybe I'm remembering the first version of the async schema API and it was eventually changed to follow the pattern and use multiple overloads?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that we could do just one overload with two optional parameters. My logic here was that explicitly specifying the behavior is a relatively rare thing, so it would be good to allow invoking with a cancellation token without having to either specify the behavior or use a named parameter for the token. The same applies for consistency with DbCommand, where you can invoke ExecuteReaderAsync this way.

We did have a discussion about the GetSchemaAsync overloads where we decided to leave them all virtual, but I think that's different (here all the public methods are non-virtual, and call a single abstract protected method without optional parameters).

{
public abstract string CommandText { get; set; }
public abstract CommandType CommandType { get; set; }
public abstract int RecordsAffected { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public setter seems odd; should this be protected set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, there definitely shouldn't be a public setter. Though rather than making the setter protected I've removed it entirely, to not impose things on implementations (this is also how DbDataReader.RecordsAffected is defined.

public abstract System.Threading.Tasks.Task PrepareAsync(System.Threading.CancellationToken cancellationToken = default);
public abstract void Cancel();
public System.Data.Common.DbBatchCommand CreateBatchCommand() { throw null; }
protected virtual System.Data.Common.DbBatchCommand CreateDbBatchCommand() { throw null; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected virtual System.Data.Common.DbBatchCommand CreateDbBatchCommand() { throw null; }
protected abstract System.Data.Common.DbBatchCommand CreateDbBatchCommand() { throw null; }

Shouldn't it be required for derived classes to implement this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch here too. CreateDbBatch on DbConnection and DbProviderFactory are virtual (and throw NotSupportedException by default) since we can't add abstract methods to existing types without breaking. But DbBatch is a new type, and it makes no sense to define an implementation of it which doesn't support creating a DbBatchCommand.

@roji roji merged commit 607f98c into dotnet:main Jul 4, 2021
roji added a commit that referenced this pull request Jul 29, 2021
Allow implementations of DbBatchCommandCollection to hide the indexer
with a narrower-typed version for their own implementation of
DbBatchCommand, as elsewhere in ADO.NET.

Fixup for #54467, part of #28633
roji added a commit that referenced this pull request Jul 29, 2021
Allow implementations of DbBatchCommandCollection to hide the indexer
with a narrower-typed version for their own implementation of
DbBatchCommand, as elsewhere in ADO.NET.

Fixup for #54467, part of #28633
@ghost ghost locked as resolved and limited conversation to collaborators Aug 28, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New System.Data.Common batching API
4 participants