diff --git a/FollowingFileStream.APIdoc/docfx.json b/FollowingFileStream.APIdoc/docfx.json index 269d171..868ac7e 100644 --- a/FollowingFileStream.APIdoc/docfx.json +++ b/FollowingFileStream.APIdoc/docfx.json @@ -17,7 +17,7 @@ "disableGitFeatures": false, "disableDefaultFilter": false, "properties": { - "TargetFramework": "netstandard2.0" + "TargetFramework": "netstandard2.1" } } ], diff --git a/FollowingFileStream.ConsoleTestTool/FollowingFileStream.ConsoleTestTool.csproj b/FollowingFileStream.ConsoleTestTool/FollowingFileStream.ConsoleTestTool.csproj index c35198b..23d755e 100644 --- a/FollowingFileStream.ConsoleTestTool/FollowingFileStream.ConsoleTestTool.csproj +++ b/FollowingFileStream.ConsoleTestTool/FollowingFileStream.ConsoleTestTool.csproj @@ -6,7 +6,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0;netcoreapp2.2 7.1 false diff --git a/FollowingFileStream.Tests/FollowingFileStream.Tests.csproj b/FollowingFileStream.Tests/FollowingFileStream.Tests.csproj index 793ab8a..21cdc27 100644 --- a/FollowingFileStream.Tests/FollowingFileStream.Tests.csproj +++ b/FollowingFileStream.Tests/FollowingFileStream.Tests.csproj @@ -1,6 +1,6 @@ - netcoreapp2.2 + netcoreapp3.0;netcoreapp2.2 false diff --git a/FollowingFileStream/AsyncStream.cs b/FollowingFileStream/AsyncStream.cs index dcb12c8..134f7b3 100644 --- a/FollowingFileStream/AsyncStream.cs +++ b/FollowingFileStream/AsyncStream.cs @@ -9,6 +9,7 @@ namespace Manandre.IO /// /// /// + #pragma warning disable S3881 public abstract class AsyncStream : Stream { #if !NETSTANDARD1_3 @@ -286,8 +287,28 @@ public sealed override void Write(byte[] buffer, int offset, int count) /// public abstract override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); - private bool disposed = false; +#if NETSTANDARD2_1 + /// + /// Asynchronously releases the unmanaged resources used by the FollowingFileStream and optionally + /// releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// + protected virtual ValueTask DisposeAsync(bool disposing) => default; + + /// + /// Asynchronously releases all resources used by the AsyncStream. + /// + public sealed override ValueTask DisposeAsync() => DisposeAsync(true); + /// + /// Releases the unmanaged resources used by the FollowingFileStream and optionally + /// releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// + protected sealed override void Dispose(bool disposing) => DisposeAsync(disposing).GetAwaiter().GetResult(); +#else /// /// Releases the unmanaged resources used by the FollowingFileStream and optionally /// releases the managed resources. @@ -296,14 +317,10 @@ public sealed override void Write(byte[] buffer, int offset, int count) /// protected override void Dispose(bool disposing) { - if (disposed) - return; - - disposed = true; // Call stream class implementation. base.Dispose(disposing); } - +#endif /// /// Synchronized version of an async stream /// @@ -400,26 +417,6 @@ public override int WriteTimeout } } - protected override void Dispose(bool disposing) - { - try - { - // Explicitly pick up a potentially methodimpl'ed Dispose - if (disposing) - { - cts.Cancel(); - using (locker.Lock()) - { - ((IDisposable)_stream).Dispose(); - } - } - } - finally - { - base.Dispose(disposing); - } - } - public override long Seek(long offset, SeekOrigin origin) { using (locker.Lock(cts.Token)) @@ -481,9 +478,64 @@ public override async Task FlushAsync(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); } } + + private bool disposed = false; + +#if NETSTANDARD2_1 + protected override async ValueTask DisposeAsync(bool disposing) + { + if (disposed) + return; + + try + { + // Explicitly pick up a potentially methodimpl'ed DisposeAsync + if (disposing) + { + cts.Cancel(); + using (await locker.LockAsync()) + { + await ((IAsyncDisposable)_stream).DisposeAsync(); + } + } + } + finally + { + disposed = true; + await base.DisposeAsync(disposing); + } + } +#else + protected override void Dispose(bool disposing) + { + if (disposed) + return; + + try + { + // Explicitly pick up a potentially methodimpl'ed Dispose + if (disposing) + { + cts.Cancel(); + using (locker.Lock()) + { + ((IDisposable)_stream).Dispose(); + } + + } + } + finally + { + disposed = true; + base.Dispose(disposing); + } + } +#endif } } + #pragma warning restore S3881 + /// /// AsyncStream class extensions /// diff --git a/FollowingFileStream/FollowingFileStream.cs b/FollowingFileStream/FollowingFileStream.cs index c1d6153..d5a3b59 100644 --- a/FollowingFileStream/FollowingFileStream.cs +++ b/FollowingFileStream/FollowingFileStream.cs @@ -63,7 +63,9 @@ public class FollowingFileStream : AsyncStream /// public FollowingFileStream(string path) { +#pragma warning disable S2930 fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); +#pragma warning restore S2930 } /// @@ -120,7 +122,9 @@ public FollowingFileStream(string path) /// public FollowingFileStream(string path, int bufferSize, bool useAsync) { +#pragma warning disable S2930 fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize, useAsync); +#pragma warning restore S2930 } #endregion @@ -324,8 +328,36 @@ private bool IsFileLockedForWriting() return false; } - bool disposed = false; + private bool disposed = false; +#if NETSTANDARD2_1 + /// + /// Releases the unmanaged resources used by the FollowingFileStream and optionally + /// releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// + protected override async ValueTask DisposeAsync(bool disposing) + { + if (disposed) + return; + + try + { + if (disposing) + { + cts.Cancel(); + await fileStream.DisposeAsync(); + } + } + finally + { + disposed = true; + // Call stream class implementation. + base.Dispose(disposing); + } + } +#else /// /// Releases the unmanaged resources used by the FollowingFileStream and optionally /// releases the managed resources. @@ -347,7 +379,7 @@ protected override void Dispose(bool disposing) // Call stream class implementation. base.Dispose(disposing); } - +#endif /// /// Clears buffers for this stream and causes any buffered data to be written to the file. /// diff --git a/FollowingFileStream/FollowingFileStream.csproj b/FollowingFileStream/FollowingFileStream.csproj index 51ed82b..32c443d 100644 --- a/FollowingFileStream/FollowingFileStream.csproj +++ b/FollowingFileStream/FollowingFileStream.csproj @@ -1,7 +1,7 @@ Library - netstandard2.0;netstandard1.3 + netstandard2.1;netstandard2.0;netstandard1.3 bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cac8b84..8bb09ad 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -36,6 +36,16 @@ jobs: projectVersion: '$(Build.BuildId)' extraProperties: 'sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/coverage/coverage.opencover.xml' + - task: UseDotNet@2 + displayName: 'Use dotnet sdk 2.x' + inputs: + version: 2.x + + - task: UseDotNet@2 + displayName: 'Use dotnet sdk 3.x' + inputs: + version: 3.x + - task: DotNetCoreCLI@2 displayName: Restore inputs: @@ -158,6 +168,16 @@ jobs: command: 'install' installPackageId: 'wkhtmltopdf' + - task: UseDotNet@2 + displayName: 'Use dotnet sdk 2.x' + inputs: + version: 2.x + + - task: UseDotNet@2 + displayName: 'Use dotnet sdk 3.x' + inputs: + version: 3.x + # First restore to resolve external dependencies - task: DotNetCoreCLI@2 displayName: Restore @@ -191,4 +211,4 @@ jobs: inputs: PathtoPublish: '$(Build.SourcesDirectory)/FollowingFileStream.APIdoc/_site_pdf/FollowingFileStream.APIdoc_pdf.pdf' ArtifactName: 'apidoc_pdf' - publishLocation: 'Container' \ No newline at end of file + publishLocation: 'Container'