Skip to content

Commit ba278f5

Browse files
authored
add nameless OpenOutgoingAttachment (#796)
* add nameless OpenOutgoingAttachment * Update Directory.Build.props
1 parent b8d8532 commit ba278f5

4 files changed

Lines changed: 158 additions & 1 deletion

File tree

src/Attachments.FileShare/Outgoing/OpenOutgoingAttachmentExtensions.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,39 @@ public static Task<Stream> OpenOutgoingAttachment(
4444
IReadOnlyDictionary<string, string>? metadata = null) =>
4545
OpenForOptions(context, options, name, timeToKeep, metadata);
4646

47+
/// <summary>
48+
/// See <see cref="OpenOutgoingAttachment(HandlerContext,SendOptions,string,GetTimeToKeep?,IReadOnlyDictionary{string,string}?)"/>.
49+
/// The attachment is registered with the default name.
50+
/// </summary>
51+
public static Task<Stream> OpenOutgoingAttachment(
52+
this HandlerContext context,
53+
SendOptions options,
54+
GetTimeToKeep? timeToKeep = null,
55+
IReadOnlyDictionary<string, string>? metadata = null) =>
56+
OpenForOptions(context, options, "default", timeToKeep, metadata);
57+
58+
/// <summary>
59+
/// See <see cref="OpenOutgoingAttachment(HandlerContext,SendOptions,string,GetTimeToKeep?,IReadOnlyDictionary{string,string}?)"/>.
60+
/// The attachment is registered with the default name.
61+
/// </summary>
62+
public static Task<Stream> OpenOutgoingAttachment(
63+
this HandlerContext context,
64+
ReplyOptions options,
65+
GetTimeToKeep? timeToKeep = null,
66+
IReadOnlyDictionary<string, string>? metadata = null) =>
67+
OpenForOptions(context, options, "default", timeToKeep, metadata);
68+
69+
/// <summary>
70+
/// See <see cref="OpenOutgoingAttachment(HandlerContext,SendOptions,string,GetTimeToKeep?,IReadOnlyDictionary{string,string}?)"/>.
71+
/// The attachment is registered with the default name.
72+
/// </summary>
73+
public static Task<Stream> OpenOutgoingAttachment(
74+
this HandlerContext context,
75+
PublishOptions options,
76+
GetTimeToKeep? timeToKeep = null,
77+
IReadOnlyDictionary<string, string>? metadata = null) =>
78+
OpenForOptions(context, options, "default", timeToKeep, metadata);
79+
4780
static async Task<Stream> OpenForOptions(
4881
HandlerContext context,
4982
ExtendableOptions options,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
3+
[NotInParallel]
4+
public class OpenOutgoingAttachmentDefaultNameTests
5+
{
6+
static readonly TestState state = new();
7+
8+
[Test]
9+
public async Task RoundTripsWithDefaultName()
10+
{
11+
state.Bytes = null;
12+
state.Reply.Reset();
13+
14+
await using var database = await Connection.SqlInstance.Build("OpenDefaultName");
15+
var connectionString = database.ConnectionString;
16+
var databaseName = new SqlConnectionStringBuilder(connectionString).InitialCatalog;
17+
18+
var configuration = new EndpointConfiguration("SqlOpenDefaultName");
19+
SqlConnection NewConnection() => new(connectionString);
20+
var attachments = configuration.EnableAttachments(NewConnection, TimeToKeep.Default, database: databaseName, table: "Attachments");
21+
configuration.UseSerialization<SystemJsonSerializer>();
22+
configuration.UsePersistence<LearningPersistence>();
23+
configuration.DisableRetries();
24+
configuration.EnableInstallers();
25+
configuration.PurgeOnStartup(true);
26+
attachments.DisableCleanupTask();
27+
configuration.RegisterComponents(_ => _.AddSingleton(state));
28+
var transport = configuration.UseTransport<LearningTransport>();
29+
transport.StorageDirectory(Path.Combine(Path.GetTempPath(), "OpenDefaultName"));
30+
transport.Transactions(TransportTransactionMode.SendsAtomicWithReceive);
31+
32+
var endpoint = await Endpoint.Start(configuration);
33+
34+
await endpoint.SendLocal(new InMessage());
35+
36+
if (!state.Reply.WaitOne(TimeSpan.FromSeconds(20)))
37+
{
38+
await endpoint.Stop();
39+
throw new("TimedOut");
40+
}
41+
42+
await endpoint.Stop();
43+
44+
await Assert.That(Encoding.UTF8.GetString(state.Bytes!)).IsEqualTo("HELLO");
45+
}
46+
47+
class TestState
48+
{
49+
public byte[]? Bytes;
50+
public ManualResetEvent Reply = new(false);
51+
}
52+
53+
class InMessage :
54+
IMessage;
55+
56+
class OutMessage :
57+
IMessage;
58+
59+
class InHandler :
60+
IHandleMessages<InMessage>
61+
{
62+
public async Task Handle(InMessage message, HandlerContext context)
63+
{
64+
var sendOptions = new SendOptions();
65+
sendOptions.RouteToThisEndpoint();
66+
67+
// No-name overload registers the attachment under the default name.
68+
await using (var sink = await context.OpenOutgoingAttachment(sendOptions))
69+
{
70+
await using var writer = new StreamWriter(sink, leaveOpen: true);
71+
await writer.WriteAsync("HELLO");
72+
}
73+
74+
await context.Send(new OutMessage(), sendOptions);
75+
}
76+
}
77+
78+
class OutHandler :
79+
IHandleMessages<OutMessage>
80+
{
81+
public async Task Handle(OutMessage message, HandlerContext context)
82+
{
83+
// No-name CopyTo reads the default-named attachment, proving the round-trip.
84+
var incoming = context.Attachments();
85+
await using var memoryStream = new MemoryStream();
86+
await incoming.CopyTo(memoryStream, context.CancellationToken);
87+
state.Bytes = memoryStream.ToArray();
88+
state.Reply.Set();
89+
}
90+
}
91+
}

src/Attachments.Sql/Outgoing/OpenOutgoingAttachmentExtensions.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,39 @@ public static Task<Stream> OpenOutgoingAttachment(
4747
IReadOnlyDictionary<string, string>? metadata = null) =>
4848
OpenForOptions(context, options, name, timeToKeep, metadata);
4949

50+
/// <summary>
51+
/// See <see cref="OpenOutgoingAttachment(HandlerContext,SendOptions,string,GetTimeToKeep?,IReadOnlyDictionary{string,string}?)"/>.
52+
/// The attachment is registered with the default name.
53+
/// </summary>
54+
public static Task<Stream> OpenOutgoingAttachment(
55+
this HandlerContext context,
56+
SendOptions options,
57+
GetTimeToKeep? timeToKeep = null,
58+
IReadOnlyDictionary<string, string>? metadata = null) =>
59+
OpenForOptions(context, options, "default", timeToKeep, metadata);
60+
61+
/// <summary>
62+
/// See <see cref="OpenOutgoingAttachment(HandlerContext,SendOptions,string,GetTimeToKeep?,IReadOnlyDictionary{string,string}?)"/>.
63+
/// The attachment is registered with the default name.
64+
/// </summary>
65+
public static Task<Stream> OpenOutgoingAttachment(
66+
this HandlerContext context,
67+
ReplyOptions options,
68+
GetTimeToKeep? timeToKeep = null,
69+
IReadOnlyDictionary<string, string>? metadata = null) =>
70+
OpenForOptions(context, options, "default", timeToKeep, metadata);
71+
72+
/// <summary>
73+
/// See <see cref="OpenOutgoingAttachment(HandlerContext,SendOptions,string,GetTimeToKeep?,IReadOnlyDictionary{string,string}?)"/>.
74+
/// The attachment is registered with the default name.
75+
/// </summary>
76+
public static Task<Stream> OpenOutgoingAttachment(
77+
this HandlerContext context,
78+
PublishOptions options,
79+
GetTimeToKeep? timeToKeep = null,
80+
IReadOnlyDictionary<string, string>? metadata = null) =>
81+
OpenForOptions(context, options, "default", timeToKeep, metadata);
82+
5083
static async Task<Stream> OpenForOptions(
5184
HandlerContext context,
5285
ExtendableOptions options,

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
<Project>
33
<PropertyGroup>
4-
<Version>19.3.0</Version>
4+
<Version>19.4.0</Version>
55
<LangVersion>preview</LangVersion>
66
<AssemblyVersion>1.0.0</AssemblyVersion>
77
<PackageTags>NServiceBus, Attachments, DataBus</PackageTags>

0 commit comments

Comments
 (0)