Fixed Saga loading and message handler filtering
Added Saga publisher extension
This commit is contained in:
parent
7bafd2f3c4
commit
5e24a2a336
@ -11,6 +11,8 @@ namespace Tapeti.Config
|
|||||||
{
|
{
|
||||||
Type MessageClass { get; set; }
|
Type MessageClass { get; set; }
|
||||||
IReadOnlyList<IBindingParameter> Parameters { get; }
|
IReadOnlyList<IBindingParameter> Parameters { get; }
|
||||||
|
|
||||||
|
void Use(IMessageMiddleware middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ namespace Tapeti.Config
|
|||||||
MethodInfo Method { get; }
|
MethodInfo Method { get; }
|
||||||
Type MessageClass { get; }
|
Type MessageClass { get; }
|
||||||
|
|
||||||
|
IReadOnlyList<IMessageMiddleware> MessageMiddleware { get; }
|
||||||
|
|
||||||
bool Accept(object message);
|
bool Accept(object message);
|
||||||
Task<object> Invoke(IMessageContext context, object message);
|
Task<object> Invoke(IMessageContext context, object message);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
namespace Tapeti.Config
|
namespace Tapeti.Config
|
||||||
{
|
{
|
||||||
public interface IMessageContext
|
public interface IMessageContext
|
||||||
{
|
{
|
||||||
|
IDependencyResolver DependencyResolver { get; }
|
||||||
|
|
||||||
object Controller { get; }
|
object Controller { get; }
|
||||||
object Message { get; }
|
object Message { get; }
|
||||||
|
IBasicProperties Properties { get; }
|
||||||
|
|
||||||
IDictionary<string, object> Items { get; }
|
IDictionary<string, object> Items { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Tapeti.Config
|
namespace Tapeti.Config
|
||||||
{
|
{
|
||||||
public interface IMessageMiddleware
|
public interface IMessageMiddleware
|
||||||
{
|
{
|
||||||
void Handle(IMessageContext context, Action next);
|
Task Handle(IMessageContext context, Func<Task> next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using RabbitMQ.Client;
|
using RabbitMQ.Client;
|
||||||
using Tapeti.Config;
|
using Tapeti.Config;
|
||||||
using Tapeti.Helpers;
|
using Tapeti.Helpers;
|
||||||
@ -33,25 +34,31 @@ namespace Tapeti.Connection
|
|||||||
if (message == null)
|
if (message == null)
|
||||||
throw new ArgumentException("Empty message");
|
throw new ArgumentException("Empty message");
|
||||||
|
|
||||||
var handled = false;
|
var validMessageType = false;
|
||||||
foreach (var binding in bindings.Where(b => b.Accept(message)))
|
foreach (var binding in bindings.Where(b => b.Accept(message)))
|
||||||
{
|
{
|
||||||
var context = new MessageContext
|
var context = new MessageContext
|
||||||
{
|
{
|
||||||
|
DependencyResolver = dependencyResolver,
|
||||||
Controller = dependencyResolver.Resolve(binding.Controller),
|
Controller = dependencyResolver.Resolve(binding.Controller),
|
||||||
Message = message
|
Message = message,
|
||||||
|
Properties = properties
|
||||||
};
|
};
|
||||||
|
|
||||||
MiddlewareHelper.Go(messageMiddleware, (handler, next) => handler.Handle(context, next));
|
MiddlewareHelper.GoAsync(binding.MessageMiddleware != null ? messageMiddleware.Concat(binding.MessageMiddleware).ToList() : messageMiddleware,
|
||||||
|
async (handler, next) => await handler.Handle(context, next),
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
var result = binding.Invoke(context, message).Result;
|
var result = binding.Invoke(context, message).Result;
|
||||||
if (result != null)
|
if (result != null)
|
||||||
worker.Publish(result);
|
await worker.Publish(result, null);
|
||||||
|
}
|
||||||
|
).Wait();
|
||||||
|
|
||||||
handled = true;
|
validMessageType = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled)
|
if (!validMessageType)
|
||||||
throw new ArgumentException($"Unsupported message type: {message.GetType().FullName}");
|
throw new ArgumentException($"Unsupported message type: {message.GetType().FullName}");
|
||||||
|
|
||||||
worker.Respond(deliveryTag, ConsumeResponse.Ack);
|
worker.Respond(deliveryTag, ConsumeResponse.Ack);
|
||||||
@ -66,8 +73,12 @@ namespace Tapeti.Connection
|
|||||||
|
|
||||||
protected class MessageContext : IMessageContext
|
protected class MessageContext : IMessageContext
|
||||||
{
|
{
|
||||||
|
public IDependencyResolver DependencyResolver { get; set; }
|
||||||
|
|
||||||
public object Controller { get; set; }
|
public object Controller { get; set; }
|
||||||
public object Message { get; set; }
|
public object Message { get; set; }
|
||||||
|
public IBasicProperties Properties { get; set; }
|
||||||
|
|
||||||
public IDictionary<string, object> Items { get; } = new Dictionary<string, object>();
|
public IDictionary<string, object> Items { get; } = new Dictionary<string, object>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
namespace Tapeti.Connection
|
namespace Tapeti.Connection
|
||||||
{
|
{
|
||||||
public class TapetiPublisher : IPublisher
|
public class TapetiPublisher : IAdvancedPublisher
|
||||||
{
|
{
|
||||||
private readonly Func<TapetiWorker> workerFactory;
|
private readonly Func<TapetiWorker> workerFactory;
|
||||||
|
|
||||||
@ -16,7 +17,13 @@ namespace Tapeti.Connection
|
|||||||
|
|
||||||
public Task Publish(object message)
|
public Task Publish(object message)
|
||||||
{
|
{
|
||||||
return workerFactory().Publish(message);
|
return workerFactory().Publish(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task Publish(object message, IBasicProperties properties)
|
||||||
|
{
|
||||||
|
return workerFactory().Publish(message, properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,16 +32,19 @@ namespace Tapeti.Connection
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task Publish(object message)
|
public Task Publish(object message, IBasicProperties properties)
|
||||||
{
|
{
|
||||||
return taskQueue.Value.Add(async () =>
|
return taskQueue.Value.Add(async () =>
|
||||||
{
|
{
|
||||||
var properties = new BasicProperties();
|
var messageProperties = properties ?? new BasicProperties();
|
||||||
var body = messageSerializer.Serialize(message, properties);
|
if (messageProperties.Timestamp.UnixTime == 0)
|
||||||
|
messageProperties.Timestamp = new AmqpTimestamp(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds());
|
||||||
|
|
||||||
|
var body = messageSerializer.Serialize(message, messageProperties);
|
||||||
|
|
||||||
(await GetChannel())
|
(await GetChannel())
|
||||||
.BasicPublish(Exchange, routingKeyStrategy.GetRoutingKey(message.GetType()), false,
|
.BasicPublish(Exchange, routingKeyStrategy.GetRoutingKey(message.GetType()), false,
|
||||||
properties, body);
|
messageProperties, body);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Tapeti.Config;
|
|
||||||
|
|
||||||
namespace Tapeti.Default
|
|
||||||
{
|
|
||||||
// End of the line...
|
|
||||||
public class BindingBufferStop : IBindingMiddleware
|
|
||||||
{
|
|
||||||
public void Handle(IBindingContext context, Action next)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -47,8 +47,8 @@ namespace Tapeti.Default
|
|||||||
{
|
{
|
||||||
object typeName;
|
object typeName;
|
||||||
|
|
||||||
if (!properties.ContentType.Equals(ContentType))
|
if (properties.ContentType == null || !properties.ContentType.Equals(ContentType))
|
||||||
throw new ArgumentException("content_type must be {ContentType}");
|
throw new ArgumentException($"content_type must be {ContentType}");
|
||||||
|
|
||||||
if (properties.Headers == null || !properties.Headers.TryGetValue(ClassTypeHeader, out typeName))
|
if (properties.Headers == null || !properties.Headers.TryGetValue(ClassTypeHeader, out typeName))
|
||||||
throw new ArgumentException($"{ClassTypeHeader} header not present");
|
throw new ArgumentException($"{ClassTypeHeader} header not present");
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Eventing.Reader;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Tapeti.Helpers
|
namespace Tapeti.Helpers
|
||||||
{
|
{
|
||||||
public static class MiddlewareHelper
|
public static class MiddlewareHelper
|
||||||
{
|
{
|
||||||
public static void Go<T>(IReadOnlyList<T> middleware, Action<T, Action> handle)
|
public static void Go<T>(IReadOnlyList<T> middleware, Action<T, Action> handle, Action lastHandler)
|
||||||
{
|
{
|
||||||
var handlerIndex = middleware.Count - 1;
|
var handlerIndex = middleware.Count - 1;
|
||||||
if (handlerIndex == -1)
|
if (handlerIndex == -1)
|
||||||
|
{
|
||||||
|
lastHandler();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Action handleNext = null;
|
Action handleNext = null;
|
||||||
|
|
||||||
@ -18,9 +23,35 @@ namespace Tapeti.Helpers
|
|||||||
handlerIndex--;
|
handlerIndex--;
|
||||||
if (handlerIndex >= 0)
|
if (handlerIndex >= 0)
|
||||||
handle(middleware[handlerIndex], handleNext);
|
handle(middleware[handlerIndex], handleNext);
|
||||||
|
else
|
||||||
|
lastHandler();
|
||||||
};
|
};
|
||||||
|
|
||||||
handle(middleware[handlerIndex], handleNext);
|
handle(middleware[handlerIndex], handleNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task GoAsync<T>(IReadOnlyList<T> middleware, Func<T, Func<Task>, Task> handle, Func<Task> lastHandler)
|
||||||
|
{
|
||||||
|
var handlerIndex = middleware.Count - 1;
|
||||||
|
if (handlerIndex == -1)
|
||||||
|
{
|
||||||
|
await lastHandler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Func<Task> handleNext = null;
|
||||||
|
|
||||||
|
handleNext = async () =>
|
||||||
|
{
|
||||||
|
handlerIndex--;
|
||||||
|
if (handlerIndex >= 0)
|
||||||
|
await handle(middleware[handlerIndex], handleNext);
|
||||||
|
else
|
||||||
|
await lastHandler();
|
||||||
|
};
|
||||||
|
|
||||||
|
await handle(middleware[handlerIndex], handleNext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
namespace Tapeti
|
namespace Tapeti
|
||||||
{
|
{
|
||||||
@ -6,4 +7,10 @@ namespace Tapeti
|
|||||||
{
|
{
|
||||||
Task Publish(object message);
|
Task Publish(object message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IAdvancedPublisher : IPublisher
|
||||||
|
{
|
||||||
|
Task Publish(object message, IBasicProperties properties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,5 @@ namespace Tapeti.Saga
|
|||||||
{
|
{
|
||||||
Task<ISaga<T>> Begin<T>(T initialState) where T : class;
|
Task<ISaga<T>> Begin<T>(T initialState) where T : class;
|
||||||
Task<ISaga<T>> Continue<T>(string sagaId) where T : class;
|
Task<ISaga<T>> Continue<T>(string sagaId) where T : class;
|
||||||
Task<object> Continue(string sagaId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Tapeti.Config;
|
|
||||||
|
|
||||||
namespace Tapeti.Saga
|
|
||||||
{
|
|
||||||
public class SagaBindingMiddleware : IBindingMiddleware
|
|
||||||
{
|
|
||||||
public void Handle(IBindingContext context, Action next)
|
|
||||||
{
|
|
||||||
foreach (var parameter in context.Parameters.Where(p =>
|
|
||||||
p.Info.ParameterType.IsGenericType &&
|
|
||||||
p.Info.ParameterType.GetGenericTypeDefinition() == typeof(ISaga<>)))
|
|
||||||
{
|
|
||||||
parameter.SetBinding(messageContext =>
|
|
||||||
{
|
|
||||||
object saga;
|
|
||||||
if (!messageContext.Items.TryGetValue("Saga", out saga))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return saga.GetType() == typeof(ISaga<>) ? saga : null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
16
Tapeti.Saga/SagaExtensions.cs
Normal file
16
Tapeti.Saga/SagaExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using RabbitMQ.Client.Framing;
|
||||||
|
|
||||||
|
namespace Tapeti.Saga
|
||||||
|
{
|
||||||
|
public static class SagaExtensions
|
||||||
|
{
|
||||||
|
public static Task Publish<T>(this IPublisher publisher, object message, ISaga<T> saga) where T : class
|
||||||
|
{
|
||||||
|
return ((IAdvancedPublisher)publisher).Publish(message, new BasicProperties
|
||||||
|
{
|
||||||
|
CorrelationId = saga.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Tapeti.Config;
|
|
||||||
|
|
||||||
namespace Tapeti.Saga
|
|
||||||
{
|
|
||||||
public class SagaMessageMiddleware : IMessageMiddleware
|
|
||||||
{
|
|
||||||
private readonly IDependencyResolver dependencyResolver;
|
|
||||||
|
|
||||||
|
|
||||||
public SagaMessageMiddleware(IDependencyResolver dependencyResolver)
|
|
||||||
{
|
|
||||||
this.dependencyResolver = dependencyResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(IMessageContext context, Action next)
|
|
||||||
{
|
|
||||||
context.Items["Saga"] = dependencyResolver.Resolve<ISagaProvider>().Continue("");
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,70 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Tapeti.Config;
|
using Tapeti.Config;
|
||||||
|
|
||||||
namespace Tapeti.Saga
|
namespace Tapeti.Saga
|
||||||
{
|
{
|
||||||
public class SagaMiddleware : IMiddlewareBundle
|
public class SagaMiddleware : IMiddlewareBundle
|
||||||
{
|
{
|
||||||
|
private const string SagaContextKey = "Saga";
|
||||||
|
|
||||||
|
|
||||||
public IEnumerable<object> GetContents(IDependencyResolver dependencyResolver)
|
public IEnumerable<object> GetContents(IDependencyResolver dependencyResolver)
|
||||||
{
|
{
|
||||||
(dependencyResolver as IDependencyInjector)?.RegisterDefault<ISagaProvider, SagaProvider>();
|
(dependencyResolver as IDependencyInjector)?.RegisterDefault<ISagaProvider, SagaProvider>();
|
||||||
|
|
||||||
yield return new SagaBindingMiddleware();
|
yield return new SagaBindingMiddleware();
|
||||||
yield return new SagaMessageMiddleware(dependencyResolver);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected class SagaBindingMiddleware : IBindingMiddleware
|
||||||
|
{
|
||||||
|
public void Handle(IBindingContext context, Action next)
|
||||||
|
{
|
||||||
|
var registered = false;
|
||||||
|
|
||||||
|
foreach (var parameter in context.Parameters.Where(p =>
|
||||||
|
p.Info.ParameterType.IsGenericType &&
|
||||||
|
p.Info.ParameterType.GetGenericTypeDefinition() == typeof(ISaga<>)))
|
||||||
|
{
|
||||||
|
if (!registered)
|
||||||
|
{
|
||||||
|
var sagaType = parameter.Info.ParameterType.GetGenericArguments()[0];
|
||||||
|
var middlewareType = typeof(SagaMessageMiddleware<>).MakeGenericType(sagaType);
|
||||||
|
|
||||||
|
context.Use(Activator.CreateInstance(middlewareType) as IMessageMiddleware);
|
||||||
|
|
||||||
|
registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter.SetBinding(messageContext =>
|
||||||
|
{
|
||||||
|
object saga;
|
||||||
|
return messageContext.Items.TryGetValue(SagaContextKey, out saga) ? saga : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected class SagaMessageMiddleware<T> : IMessageMiddleware where T : class
|
||||||
|
{
|
||||||
|
public async Task Handle(IMessageContext context, Func<Task> next)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(context.Properties.CorrelationId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var saga = await context.DependencyResolver.Resolve<ISagaProvider>().Continue<T>(context.Properties.CorrelationId);
|
||||||
|
if (saga == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
context.Items[SagaContextKey] = saga;
|
||||||
|
await next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,15 +29,6 @@ namespace Tapeti.Saga
|
|||||||
return await Saga<T>.Create(async () => await store.Read(sagaId) as T, sagaId);
|
return await Saga<T>.Create(async () => await store.Read(sagaId) as T, sagaId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> Continue(string sagaId)
|
|
||||||
{
|
|
||||||
return new Saga<object>
|
|
||||||
{
|
|
||||||
Id = sagaId,
|
|
||||||
State = await store.Read(sagaId)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected class Saga<T> : ISaga<T> where T : class
|
protected class Saga<T> : ISaga<T> where T : class
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="RabbitMQ.Client, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89e7d7c5feba84ce" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@ -45,11 +46,10 @@
|
|||||||
<Compile Include="ISagaProvider.cs" />
|
<Compile Include="ISagaProvider.cs" />
|
||||||
<Compile Include="ISagaStore.cs" />
|
<Compile Include="ISagaStore.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="SagaBindingMiddleware.cs" />
|
|
||||||
<Compile Include="SagaMemoryStore.cs" />
|
<Compile Include="SagaMemoryStore.cs" />
|
||||||
<Compile Include="SagaMessageMiddleware.cs" />
|
|
||||||
<Compile Include="SagaMiddleware.cs" />
|
<Compile Include="SagaMiddleware.cs" />
|
||||||
<Compile Include="SagaProvider.cs" />
|
<Compile Include="SagaProvider.cs" />
|
||||||
|
<Compile Include="SagaExtensions.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Tapeti.csproj">
|
<ProjectReference Include="..\Tapeti.csproj">
|
||||||
|
@ -64,7 +64,6 @@
|
|||||||
<Compile Include="IConnection.cs" />
|
<Compile Include="IConnection.cs" />
|
||||||
<Compile Include="ILogger.cs" />
|
<Compile Include="ILogger.cs" />
|
||||||
<Compile Include="Config\IMessageContext.cs" />
|
<Compile Include="Config\IMessageContext.cs" />
|
||||||
<Compile Include="Default\BindingBufferStop.cs" />
|
|
||||||
<Compile Include="Config\IMessageMiddleware.cs" />
|
<Compile Include="Config\IMessageMiddleware.cs" />
|
||||||
<Compile Include="Config\IMiddlewareBundle.cs" />
|
<Compile Include="Config\IMiddlewareBundle.cs" />
|
||||||
<Compile Include="Config\IConfig.cs" />
|
<Compile Include="Config\IConfig.cs" />
|
||||||
|
@ -36,7 +36,6 @@ namespace Tapeti
|
|||||||
this.exchange = exchange;
|
this.exchange = exchange;
|
||||||
this.dependencyResolver = dependencyResolver;
|
this.dependencyResolver = dependencyResolver;
|
||||||
|
|
||||||
Use(new BindingBufferStop());
|
|
||||||
Use(new DependencyResolverBinding(dependencyResolver));
|
Use(new DependencyResolverBinding(dependencyResolver));
|
||||||
Use(new MessageBinding());
|
Use(new MessageBinding());
|
||||||
}
|
}
|
||||||
@ -129,7 +128,8 @@ namespace Tapeti
|
|||||||
Method = method,
|
Method = method,
|
||||||
QueueInfo = methodQueueInfo,
|
QueueInfo = methodQueueInfo,
|
||||||
MessageClass = context.MessageClass,
|
MessageClass = context.MessageClass,
|
||||||
MessageHandler = messageHandler
|
MessageHandler = messageHandler,
|
||||||
|
MessageMiddleware = context.MessageMiddleware
|
||||||
};
|
};
|
||||||
|
|
||||||
if (methodQueueInfo.Dynamic.GetValueOrDefault())
|
if (methodQueueInfo.Dynamic.GetValueOrDefault())
|
||||||
@ -159,7 +159,7 @@ namespace Tapeti
|
|||||||
|
|
||||||
protected MessageHandlerFunc GetMessageHandler(IBindingContext context, MethodInfo method)
|
protected MessageHandlerFunc GetMessageHandler(IBindingContext context, MethodInfo method)
|
||||||
{
|
{
|
||||||
MiddlewareHelper.Go(bindingMiddleware, (handler, next) => handler.Handle(context, next));
|
MiddlewareHelper.Go(bindingMiddleware, (handler, next) => handler.Handle(context, next), () => {});
|
||||||
|
|
||||||
if (context.MessageClass == null)
|
if (context.MessageClass == null)
|
||||||
throw new TopologyConfigurationException($"Method {method.Name} in controller {method.DeclaringType?.Name} does not resolve to a message class");
|
throw new TopologyConfigurationException($"Method {method.Name} in controller {method.DeclaringType?.Name} does not resolve to a message class");
|
||||||
@ -336,6 +336,8 @@ namespace Tapeti
|
|||||||
public MethodInfo Method { get; set; }
|
public MethodInfo Method { get; set; }
|
||||||
public Type MessageClass { get; set; }
|
public Type MessageClass { get; set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<IMessageMiddleware> MessageMiddleware { get; set; }
|
||||||
|
|
||||||
public QueueInfo QueueInfo { get; set; }
|
public QueueInfo QueueInfo { get; set; }
|
||||||
public MessageHandlerFunc MessageHandler { get; set; }
|
public MessageHandlerFunc MessageHandler { get; set; }
|
||||||
|
|
||||||
@ -361,14 +363,26 @@ namespace Tapeti
|
|||||||
|
|
||||||
internal class BindingContext : IBindingContext
|
internal class BindingContext : IBindingContext
|
||||||
{
|
{
|
||||||
|
private List<IMessageMiddleware> messageMiddleware;
|
||||||
|
|
||||||
public Type MessageClass { get; set; }
|
public Type MessageClass { get; set; }
|
||||||
public IReadOnlyList<IBindingParameter> Parameters { get; }
|
public IReadOnlyList<IBindingParameter> Parameters { get; }
|
||||||
|
public IReadOnlyList<IMessageMiddleware> MessageMiddleware => messageMiddleware;
|
||||||
|
|
||||||
|
|
||||||
public BindingContext(IReadOnlyList<IBindingParameter> parameters)
|
public BindingContext(IReadOnlyList<IBindingParameter> parameters)
|
||||||
{
|
{
|
||||||
Parameters = parameters;
|
Parameters = parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Use(IMessageMiddleware middleware)
|
||||||
|
{
|
||||||
|
if (messageMiddleware == null)
|
||||||
|
messageMiddleware = new List<IMessageMiddleware>();
|
||||||
|
|
||||||
|
messageMiddleware.Add(middleware);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlServer.Server;
|
|
||||||
using Tapeti;
|
using Tapeti;
|
||||||
using Tapeti.Annotations;
|
using Tapeti.Annotations;
|
||||||
|
using Tapeti.Saga;
|
||||||
|
|
||||||
namespace Test
|
namespace Test
|
||||||
{
|
{
|
||||||
@ -9,33 +9,46 @@ namespace Test
|
|||||||
public class MarcoController : MessageController
|
public class MarcoController : MessageController
|
||||||
{
|
{
|
||||||
private readonly IPublisher publisher;
|
private readonly IPublisher publisher;
|
||||||
|
private readonly ISagaProvider sagaProvider;
|
||||||
|
|
||||||
|
|
||||||
public MarcoController(IPublisher publisher/*, ISagaProvider sagaProvider*/)
|
public MarcoController(IPublisher publisher, ISagaProvider sagaProvider)
|
||||||
{
|
{
|
||||||
this.publisher = publisher;
|
this.publisher = publisher;
|
||||||
|
this.sagaProvider = sagaProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//[StaticQueue("test")]
|
/*
|
||||||
public PoloMessage Marco(MarcoMessage message, Visualizer visualizer)
|
* For simple request response patterns, the return type can also be used:
|
||||||
|
|
||||||
|
public async Task<PoloMessage> Marco(MarcoMessage message, Visualizer visualizer)
|
||||||
{
|
{
|
||||||
visualizer.VisualizeMarco();
|
visualizer.VisualizeMarco();
|
||||||
|
return new PoloMessage(); ;
|
||||||
/*
|
|
||||||
using (sagaProvider.Begin<MarcoState>(new MarcoState
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
//publisher.Publish(new PoloColorRequest(), saga, PoloColorResponse1);
|
|
||||||
//publisher.Publish(new PoloColorRequest(), saga, callID = "tweede");
|
|
||||||
|
|
||||||
// Saga refcount = 2
|
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return new PoloMessage(); ;
|
// Visualizer can also be constructor injected, just proving a point here...
|
||||||
|
public async Task Marco(MarcoMessage message, Visualizer visualizer)
|
||||||
|
{
|
||||||
|
visualizer.VisualizeMarco();
|
||||||
|
|
||||||
|
using (var saga = await sagaProvider.Begin(new MarcoPoloSaga()))
|
||||||
|
{
|
||||||
|
// TODO provide publish extension with Saga support
|
||||||
|
await publisher.Publish(new PoloMessage(), saga);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Polo(PoloMessage message, Visualizer visualizer, ISaga<MarcoPoloSaga> saga)
|
||||||
|
{
|
||||||
|
if (saga.State.ReceivedPolo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
saga.State.ReceivedPolo = true;
|
||||||
|
visualizer.VisualizePolo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -61,11 +74,6 @@ namespace Test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void Polo(PoloMessage message, Visualizer visualizer)
|
|
||||||
{
|
|
||||||
visualizer.VisualizePolo();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -79,15 +87,8 @@ namespace Test
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class MarcoPoloSaga
|
||||||
|
|
||||||
public class PoloColorRequest
|
|
||||||
{
|
{
|
||||||
|
public bool ReceivedPolo;
|
||||||
}
|
|
||||||
|
|
||||||
public class PoloColorResponse
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace Test
|
|||||||
var container = new Container();
|
var container = new Container();
|
||||||
container.Register<MarcoEmitter>();
|
container.Register<MarcoEmitter>();
|
||||||
container.Register<Visualizer>();
|
container.Register<Visualizer>();
|
||||||
container.Register<ISagaStore, SagaMemoryStore>();
|
container.RegisterSingleton<ISagaStore, SagaMemoryStore>();
|
||||||
|
|
||||||
var config = new TapetiConfig("test", new SimpleInjectorDependencyResolver(container))
|
var config = new TapetiConfig("test", new SimpleInjectorDependencyResolver(container))
|
||||||
.Use(new SagaMiddleware())
|
.Use(new SagaMiddleware())
|
||||||
|
Loading…
Reference in New Issue
Block a user