Skip to content

Commit 38d6a25

Browse files
committed
Your envelope is not my envelope - easy conversion from the untyped usage on the dispatcher side to the typed usage on the handler side.
1 parent ad0cef9 commit 38d6a25

File tree

1 file changed

+57
-12
lines changed

1 file changed

+57
-12
lines changed

src/Recipes/Enveloping/Usage.cs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Linq.Expressions;
36
using System.Runtime.Caching;
47
using System.Threading.Tasks;
58
using NUnit.Framework;
@@ -21,43 +24,85 @@ public async Task Show()
2124
Resolve.WhenEqualToHandlerMessageType(new Projection().Handlers)).
2225
ProjectAsync(cache, new object[]
2326
{
24-
new Envelope<PortfolioAdded>(
27+
new Envelope(
2528
new PortfolioAdded {Id = portfolioId, Name = "My portfolio"},
2629
new Dictionary<string, object>
2730
{
2831
{"Position", 0L}
29-
}),
30-
new Envelope<PortfolioRenamed>(
32+
}).ToGenericEnvelope(),
33+
new Envelope(
3134
new PortfolioRenamed {Id = portfolioId, Name = "Your portfolio"},
3235
new Dictionary<string, object>
3336
{
3437
{"Position", 1L}
35-
}),
36-
new Envelope<PortfolioRemoved>(
38+
}).ToGenericEnvelope(),
39+
new Envelope(
3740
new PortfolioRemoved {Id = portfolioId},
3841
new Dictionary<string, object>
3942
{
4043
{"Position", 2L}
41-
})
44+
}).ToGenericEnvelope()
4245
});
4346
}
4447
}
4548

46-
class Envelope<TMessage>
49+
class Envelope<TMessage> //Used by handlers
4750
{
48-
public Envelope(TMessage message, IDictionary<string, object> metadata)
51+
private readonly Envelope _envelope;
52+
53+
public Envelope(Envelope envelope)
54+
{
55+
if (envelope == null)
56+
throw new ArgumentNullException(nameof(envelope));
57+
_envelope = envelope;
58+
}
59+
60+
public TMessage Message => (TMessage) _envelope.Message;
61+
public long Position => (long)_envelope.Metadata["Position"];
62+
}
63+
64+
class Envelope //Used by dispatchers
65+
{
66+
//Note we could precompute these factories for all known message types.
67+
private static readonly ConcurrentDictionary<Type, Func<Envelope, object>> Factories =
68+
new ConcurrentDictionary<Type, Func<Envelope, object>>();
69+
70+
public Envelope(object message, IReadOnlyDictionary<string, object> metadata)
4971
{
72+
if (message == null)
73+
throw new ArgumentNullException(nameof(message));
5074
if (metadata == null)
5175
throw new ArgumentNullException(nameof(metadata));
5276
Message = message;
5377
Metadata = metadata;
5478
}
5579

56-
public TMessage Message { get; }
57-
public IDictionary<string, object> Metadata { get; }
58-
public long Position => (long)Metadata["Position"];
80+
public object Message { get; }
81+
public IReadOnlyDictionary<string, object> Metadata { get; }
82+
83+
public object ToGenericEnvelope()
84+
{
85+
var factory = Factories
86+
.GetOrAdd(Message.GetType(), typeOfMessage =>
87+
{
88+
var parameter = Expression
89+
.Parameter(typeof(Envelope), "envelope");
90+
return Expression
91+
.Lambda<Func<Envelope, object>>(
92+
Expression.New(
93+
typeof(Envelope<>)
94+
.MakeGenericType(typeOfMessage)
95+
.GetConstructors()
96+
.Single(),
97+
parameter),
98+
parameter)
99+
.Compile();
100+
});
101+
return factory(this);
102+
}
59103
}
60104

105+
61106
class Projection : ConnectedProjection<MemoryCache>
62107
{
63108
public Projection()
@@ -79,7 +124,7 @@ public Projection()
79124
});
80125
When<Envelope<PortfolioRemoved>>((cache, envelope) =>
81126
{
82-
cache.Remove(envelope.Message.Id.ToString());
127+
'cache.Remove(envelope.Message.Id.ToString());
83128
});
84129
When<Envelope<PortfolioRenamed>>((cache, envelope) =>
85130
{

0 commit comments

Comments
 (0)