Skip to content

Commit ad7462d

Browse files
committed
Add CancellationToken to async mapper
1 parent efe8047 commit ad7462d

File tree

6 files changed

+222
-57
lines changed

6 files changed

+222
-57
lines changed

Source/Boxed.Mapping/AsyncMapperExtensions.cs

Lines changed: 77 additions & 39 deletions
Large diffs are not rendered by default.

Source/Boxed.Mapping/Boxed.Mapping.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup Label="Build">
44
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
5+
<Nullable>enable</Nullable>
56
</PropertyGroup>
67

78
<PropertyGroup Label="Package">

Source/Boxed.Mapping/IAsyncMapper.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace Boxed.Mapping
22
{
3+
using System.Threading;
34
using System.Threading.Tasks;
45

56
/// <summary>
@@ -14,7 +15,8 @@ public interface IAsyncMapper<in TSource, in TDestination>
1415
/// </summary>
1516
/// <param name="source">The source object to map from.</param>
1617
/// <param name="destination">The destination object to map to.</param>
17-
/// <returns>A task representing the operation.</returns>
18-
Task MapAsync(TSource source, TDestination destination);
18+
/// <param name="cancellationToken">The cancellation token.</param>
19+
/// <returns>A task representing the asynchronous operation.</returns>
20+
Task MapAsync(TSource source, TDestination destination, CancellationToken cancellationToken);
1921
}
2022
}

Tests/Boxed.Mapping.Test/AsyncMapper.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
namespace Boxed.Mapping.Test
22
{
33
using System;
4+
using System.Threading;
45
using System.Threading.Tasks;
56

67
public class AsyncMapper : IAsyncMapper<MapFrom, MapTo>
78
{
8-
public Task MapAsync(MapFrom from, MapTo to)
9+
public CancellationToken CancellationToken { get; private set; }
10+
11+
public Task MapAsync(MapFrom from, MapTo to, CancellationToken cancellationToken)
912
{
1013
if (from is null)
1114
{
@@ -17,6 +20,7 @@ public Task MapAsync(MapFrom from, MapTo to)
1720
throw new ArgumentNullException(nameof(to));
1821
}
1922

23+
this.CancellationToken = cancellationToken;
2024
to.Property = from.Property;
2125
return Task.FromResult<object>(null);
2226
}

Tests/Boxed.Mapping.Test/AsyncMapperTest.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,34 @@ namespace Boxed.Mapping.Test
44
using System.Collections.Generic;
55
using System.Collections.ObjectModel;
66
using System.Linq;
7+
using System.Threading;
78
using System.Threading.Tasks;
89
using Xunit;
910

10-
public class AsyncMapperTest
11+
public class AsyncMapperTest : Disposable
1112
{
13+
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
14+
1215
[Fact]
1316
public Task MapAsync_Null_ThrowsArgumentNullExceptionAsync()
1417
{
1518
var mapper = new AsyncMapper();
1619

17-
return Assert.ThrowsAsync<ArgumentNullException>("source", () => mapper.MapAsync(null));
20+
return Assert.ThrowsAsync<ArgumentNullException>(
21+
"source",
22+
() => mapper.MapAsync(null, this.cancellationTokenSource.Token));
1823
}
1924

2025
[Fact]
2126
public async Task MapAsync_ToNewObject_MappedAsync()
2227
{
2328
var mapper = new AsyncMapper();
2429

25-
var to = await mapper.MapAsync(new MapFrom() { Property = 1 }).ConfigureAwait(false);
30+
var to = await mapper
31+
.MapAsync(new MapFrom() { Property = 1 }, this.cancellationTokenSource.Token)
32+
.ConfigureAwait(false);
2633

34+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
2735
Assert.Equal(1, to.Property);
2836
}
2937

@@ -32,7 +40,9 @@ public async Task MapArrayAsync_Empty_MappedAsync()
3240
{
3341
var mapper = new AsyncMapper();
3442

35-
var to = await mapper.MapArrayAsync(Array.Empty<MapFrom>()).ConfigureAwait(false);
43+
var to = await mapper
44+
.MapArrayAsync(Array.Empty<MapFrom>(), this.cancellationTokenSource.Token)
45+
.ConfigureAwait(false);
3646

3747
Assert.IsType<MapTo[]>(to);
3848
Assert.Empty(to);
@@ -49,9 +59,11 @@ public async Task MapArrayAsync_ToNewObject_MappedAsync()
4959
{
5060
new MapFrom() { Property = 1 },
5161
new MapFrom() { Property = 2 },
52-
})
62+
},
63+
this.cancellationTokenSource.Token)
5364
.ConfigureAwait(false);
5465

66+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
5567
Assert.IsType<MapTo[]>(to);
5668
Assert.Equal(2, to.Length);
5769
Assert.Equal(1, to[0].Property);
@@ -63,7 +75,9 @@ public async Task MapTypedCollectionAsync_Empty_MappedAsync()
6375
{
6476
var mapper = new AsyncMapper();
6577

66-
var to = await mapper.MapCollectionAsync(Array.Empty<MapFrom>(), new List<MapTo>()).ConfigureAwait(false);
78+
var to = await mapper
79+
.MapCollectionAsync(Array.Empty<MapFrom>(), new List<MapTo>(), this.cancellationTokenSource.Token)
80+
.ConfigureAwait(false);
6781

6882
Assert.IsType<List<MapTo>>(to);
6983
Assert.Empty(to);
@@ -81,9 +95,11 @@ public async Task MapTypedCollectionAsync_ToNewObject_MappedAsync()
8195
new MapFrom() { Property = 1 },
8296
new MapFrom() { Property = 2 },
8397
},
84-
new List<MapTo>())
98+
new List<MapTo>(),
99+
this.cancellationTokenSource.Token)
85100
.ConfigureAwait(false);
86101

102+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
87103
Assert.IsType<List<MapTo>>(to);
88104
Assert.Equal(2, to.Count);
89105
Assert.Equal(1, to[0].Property);
@@ -95,7 +111,9 @@ public async Task MapCollectionAsync_Empty_MappedAsync()
95111
{
96112
var mapper = new AsyncMapper();
97113

98-
var to = await mapper.MapCollectionAsync(Array.Empty<MapFrom>()).ConfigureAwait(false);
114+
var to = await mapper
115+
.MapCollectionAsync(Array.Empty<MapFrom>(), this.cancellationTokenSource.Token)
116+
.ConfigureAwait(false);
99117

100118
Assert.IsType<Collection<MapTo>>(to);
101119
Assert.Empty(to);
@@ -112,9 +130,11 @@ public async Task MapCollectionAsync_ToNewObject_MappedAsync()
112130
{
113131
new MapFrom() { Property = 1 },
114132
new MapFrom() { Property = 2 },
115-
})
133+
},
134+
this.cancellationTokenSource.Token)
116135
.ConfigureAwait(false);
117136

137+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
118138
Assert.IsType<Collection<MapTo>>(to);
119139
Assert.Equal(2, to.Count);
120140
Assert.Equal(1, to[0].Property);
@@ -126,7 +146,9 @@ public async Task MapListAsync_Empty_MappedAsync()
126146
{
127147
var mapper = new AsyncMapper();
128148

129-
var to = await mapper.MapListAsync(Array.Empty<MapFrom>()).ConfigureAwait(false);
149+
var to = await mapper
150+
.MapListAsync(Array.Empty<MapFrom>(), this.cancellationTokenSource.Token)
151+
.ConfigureAwait(false);
130152

131153
Assert.IsType<List<MapTo>>(to);
132154
Assert.Empty(to);
@@ -143,9 +165,11 @@ public async Task MapListAsync_ToNewObject_MappedAsync()
143165
{
144166
new MapFrom() { Property = 1 },
145167
new MapFrom() { Property = 2 },
146-
})
168+
},
169+
this.cancellationTokenSource.Token)
147170
.ConfigureAwait(false);
148171

172+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
149173
Assert.IsType<List<MapTo>>(to);
150174
Assert.Equal(2, to.Count);
151175
Assert.Equal(1, to[0].Property);
@@ -158,7 +182,7 @@ public async Task MapObservableCollectionAsync_Empty_MappedAsync()
158182
var mapper = new AsyncMapper();
159183

160184
var to = await mapper
161-
.MapObservableCollectionAsync(Array.Empty<MapFrom>())
185+
.MapObservableCollectionAsync(Array.Empty<MapFrom>(), this.cancellationTokenSource.Token)
162186
.ConfigureAwait(false);
163187

164188
Assert.IsType<ObservableCollection<MapTo>>(to);
@@ -176,17 +200,19 @@ public async Task MapObservableCollectionAsync_ToNewObject_MappedAsync()
176200
{
177201
new MapFrom() { Property = 1 },
178202
new MapFrom() { Property = 2 },
179-
})
203+
},
204+
this.cancellationTokenSource.Token)
180205
.ConfigureAwait(false);
181206

207+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
182208
Assert.IsType<ObservableCollection<MapTo>>(to);
183209
Assert.Equal(2, to.Count);
184210
Assert.Equal(1, to[0].Property);
185211
Assert.Equal(2, to[1].Property);
186212
}
187213

188214
[Fact]
189-
public async Task MapAsyncEnumerable_ToNewObject_MappedAsync()
215+
public async Task MapEnumerableAsync_ToNewObject_MappedAsync()
190216
{
191217
var mapper = new AsyncMapper();
192218
var source = new TestAsyncEnumerable<MapFrom>(
@@ -196,13 +222,16 @@ public async Task MapAsyncEnumerable_ToNewObject_MappedAsync()
196222
new MapFrom() { Property = 2 },
197223
});
198224

199-
var to = mapper.MapEnumerableAsync(source);
225+
var to = mapper.MapEnumerableAsync(source, this.cancellationTokenSource.Token);
200226

201227
var list = await to.ToListAsync().ConfigureAwait(false);
228+
Assert.Equal(this.cancellationTokenSource.Token, mapper.CancellationToken);
202229
Assert.IsType<List<MapTo>>(list);
203230
Assert.Equal(2, list.Count);
204231
Assert.Equal(1, list[0].Property);
205232
Assert.Equal(2, list[1].Property);
206233
}
234+
235+
protected override void DisposeManaged() => this.cancellationTokenSource.Dispose();
207236
}
208237
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
namespace Boxed.Mapping.Test
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// Base class for members implementing <see cref="IDisposable"/>.
7+
/// </summary>
8+
public abstract class Disposable : IDisposable
9+
{
10+
/// <summary>
11+
/// Finalizes an instance of the <see cref="Disposable"/> class. Releases unmanaged
12+
/// resources and performs other clean-up operations before the <see cref="Disposable"/>
13+
/// is reclaimed by garbage collection. Will run only if the
14+
/// Dispose method does not get called.
15+
/// </summary>
16+
~Disposable()
17+
{
18+
this.Dispose(false);
19+
}
20+
21+
/// <summary>
22+
/// Gets a value indicating whether this <see cref="Disposable"/> is disposed.
23+
/// </summary>
24+
/// <value><c>true</c> if disposed; otherwise, <c>false</c>.</value>
25+
public bool IsDisposed { get; private set; }
26+
27+
/// <summary>
28+
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
29+
/// </summary>
30+
public void Dispose()
31+
{
32+
// Dispose all managed and unmanaged resources.
33+
this.Dispose(true);
34+
35+
// Take this object off the finalization queue and prevent finalization code for this
36+
// object from executing a second time.
37+
GC.SuppressFinalize(this);
38+
}
39+
40+
/// <summary>
41+
/// Disposes the managed resources implementing <see cref="IDisposable"/>.
42+
/// </summary>
43+
protected virtual void DisposeManaged()
44+
{
45+
}
46+
47+
/// <summary>
48+
/// Disposes the unmanaged resources implementing <see cref="IDisposable"/>.
49+
/// </summary>
50+
protected virtual void DisposeUnmanaged()
51+
{
52+
}
53+
54+
/// <summary>
55+
/// Throws a <see cref="ObjectDisposedException"/> if this instance is disposed.
56+
/// </summary>
57+
protected void ThrowIfDisposed()
58+
{
59+
if (this.IsDisposed)
60+
{
61+
throw new ObjectDisposedException(this.GetType().Name);
62+
}
63+
}
64+
65+
/// <summary>
66+
/// Releases unmanaged and - optionally - managed resources.
67+
/// </summary>
68+
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
69+
/// <c>false</c> to release only unmanaged resources, called from the finalizer only.</param>
70+
/// <remarks>We suppress CA1063 which requires that this method be protected virtual because we want to hide
71+
/// the internal implementation.</remarks>
72+
#pragma warning disable CA1063 // Implement IDisposable Correctly
73+
private void Dispose(bool disposing)
74+
#pragma warning restore CA1063 // Implement IDisposable Correctly
75+
{
76+
// Check to see if Dispose has already been called.
77+
if (!this.IsDisposed)
78+
{
79+
// If disposing managed and unmanaged resources.
80+
if (disposing)
81+
{
82+
this.DisposeManaged();
83+
}
84+
85+
this.DisposeUnmanaged();
86+
87+
this.IsDisposed = true;
88+
}
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)