Implemented stateless request-response support
This commit is contained in:
parent
bbb5f6c218
commit
2745d18779
@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<RootNamespace>_06_StatelessRequestResponse</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="SimpleInjector" Version="4.9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Examples\ExampleLib\ExampleLib.csproj" />
|
||||||
|
<ProjectReference Include="..\Examples\Messaging.TapetiExample\Messaging.TapetiExample.csproj" />
|
||||||
|
<ProjectReference Include="..\Tapeti.DataAnnotations\Tapeti.DataAnnotations.csproj" />
|
||||||
|
<ProjectReference Include="..\Tapeti.SimpleInjector\Tapeti.SimpleInjector.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
28
06-StatelessRequestResponse/ExampleMessageController.cs
Normal file
28
06-StatelessRequestResponse/ExampleMessageController.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using ExampleLib;
|
||||||
|
using Messaging.TapetiExample;
|
||||||
|
using Tapeti.Annotations;
|
||||||
|
|
||||||
|
namespace _06_StatelessRequestResponse
|
||||||
|
{
|
||||||
|
[MessageController]
|
||||||
|
[DynamicQueue("tapeti.example.06")]
|
||||||
|
public class ExampleMessageController
|
||||||
|
{
|
||||||
|
private readonly IExampleState exampleState;
|
||||||
|
|
||||||
|
|
||||||
|
public ExampleMessageController(IExampleState exampleState)
|
||||||
|
{
|
||||||
|
this.exampleState = exampleState;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[ResponseHandler]
|
||||||
|
public void HandleQuoteResponse(QuoteResponseMessage message)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Received response: " + message.Quote);
|
||||||
|
exampleState.Done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
06-StatelessRequestResponse/Program.cs
Normal file
51
06-StatelessRequestResponse/Program.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ExampleLib;
|
||||||
|
using Messaging.TapetiExample;
|
||||||
|
using SimpleInjector;
|
||||||
|
using Tapeti;
|
||||||
|
using Tapeti.DataAnnotations;
|
||||||
|
using Tapeti.Default;
|
||||||
|
using Tapeti.SimpleInjector;
|
||||||
|
|
||||||
|
namespace _06_StatelessRequestResponse
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var container = new Container();
|
||||||
|
var dependencyResolver = new SimpleInjectorDependencyResolver(container);
|
||||||
|
|
||||||
|
container.Register<ILogger, ConsoleLogger>();
|
||||||
|
|
||||||
|
var helper = new ExampleConsoleApp(dependencyResolver);
|
||||||
|
helper.Run(MainAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal static async Task MainAsync(IDependencyResolver dependencyResolver, Func<Task> waitForDone)
|
||||||
|
{
|
||||||
|
var config = new TapetiConfig(dependencyResolver)
|
||||||
|
.WithDataAnnotations()
|
||||||
|
.RegisterAllControllers()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
using (var connection = new TapetiConnection(config))
|
||||||
|
{
|
||||||
|
await connection.Subscribe();
|
||||||
|
|
||||||
|
var publisher = dependencyResolver.Resolve<IPublisher>();
|
||||||
|
await publisher.PublishRequest<ExampleMessageController, QuoteRequestMessage, QuoteResponseMessage>(
|
||||||
|
new QuoteRequestMessage
|
||||||
|
{
|
||||||
|
Amount = 1
|
||||||
|
},
|
||||||
|
c => c.HandleQuoteResponse);
|
||||||
|
|
||||||
|
await waitForDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
06-StatelessRequestResponse/ReceivingMessageController.cs
Normal file
38
06-StatelessRequestResponse/ReceivingMessageController.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using Messaging.TapetiExample;
|
||||||
|
using Tapeti.Annotations;
|
||||||
|
|
||||||
|
namespace _06_StatelessRequestResponse
|
||||||
|
{
|
||||||
|
[MessageController]
|
||||||
|
[DynamicQueue("tapeti.example.06.receiver")]
|
||||||
|
public class ReceivingMessageController
|
||||||
|
{
|
||||||
|
// No publisher required, responses can simply be returned
|
||||||
|
public QuoteResponseMessage HandleQuoteRequest(QuoteRequestMessage message)
|
||||||
|
{
|
||||||
|
string quote;
|
||||||
|
|
||||||
|
switch (message.Amount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
// Well, they asked for it... :-)
|
||||||
|
quote = "'";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
quote = "\"";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// We have to return a response.
|
||||||
|
quote = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new QuoteResponseMessage
|
||||||
|
{
|
||||||
|
Quote = quote
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Tapeti.Annotations/ResponseHandlerAttribute.cs
Normal file
14
Tapeti.Annotations/ResponseHandlerAttribute.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Tapeti.Annotations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the method only handles response messages which are sent directly
|
||||||
|
/// to the queue. No binding will be created.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class ResponseHandlerAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<Version>2.0.0</Version>
|
<Version>2.0.0</Version>
|
||||||
<Authors>Mark van Renswoude</Authors>
|
<Authors>Mark van Renswoude</Authors>
|
||||||
<Company>Mark van Renswoude</Company>
|
<Company>Mark van Renswoude</Company>
|
||||||
|
@ -55,7 +55,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.Ninject", "Tapeti.Ni
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{62002327-46B0-4B72-B95A-594CE7F8C80D}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{62002327-46B0-4B72-B95A-594CE7F8C80D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tapeti.Cmd", "Tapeti.Cmd\Tapeti.Cmd.csproj", "{C8728BFC-7F97-41BC-956B-690A57B634EC}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.Cmd", "Tapeti.Cmd\Tapeti.Cmd.csproj", "{C8728BFC-7F97-41BC-956B-690A57B634EC}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "06-StatelessRequestResponse", "06-StatelessRequestResponse\06-StatelessRequestResponse.csproj", "{152227AA-3165-4550-8997-6EA80C84516E}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -151,6 +153,10 @@ Global
|
|||||||
{C8728BFC-7F97-41BC-956B-690A57B634EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C8728BFC-7F97-41BC-956B-690A57B634EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C8728BFC-7F97-41BC-956B-690A57B634EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C8728BFC-7F97-41BC-956B-690A57B634EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C8728BFC-7F97-41BC-956B-690A57B634EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
{C8728BFC-7F97-41BC-956B-690A57B634EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{152227AA-3165-4550-8997-6EA80C84516E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{152227AA-3165-4550-8997-6EA80C84516E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{152227AA-3165-4550-8997-6EA80C84516E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{152227AA-3165-4550-8997-6EA80C84516E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -177,6 +183,7 @@ Global
|
|||||||
{BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F}
|
{BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F}
|
||||||
{29478B10-FC53-4E93-ADEF-A775D9408131} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F}
|
{29478B10-FC53-4E93-ADEF-A775D9408131} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F}
|
||||||
{C8728BFC-7F97-41BC-956B-690A57B634EC} = {62002327-46B0-4B72-B95A-594CE7F8C80D}
|
{C8728BFC-7F97-41BC-956B-690A57B634EC} = {62002327-46B0-4B72-B95A-594CE7F8C80D}
|
||||||
|
{152227AA-3165-4550-8997-6EA80C84516E} = {266B9B94-A4D2-41C2-860C-24A7C3B63B56}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {B09CC2BF-B2AF-4CB6-8728-5D1D8E5C50FA}
|
SolutionGuid = {B09CC2BF-B2AF-4CB6-8728-5D1D8E5C50FA}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Tapeti.Config
|
namespace Tapeti.Config
|
||||||
{
|
{
|
||||||
@ -81,6 +82,13 @@ namespace Tapeti.Config
|
|||||||
/// <param name="method"></param>
|
/// <param name="method"></param>
|
||||||
/// <returns>The binding if found, null otherwise</returns>
|
/// <returns>The binding if found, null otherwise</returns>
|
||||||
IControllerMethodBinding ForMethod(Delegate method);
|
IControllerMethodBinding ForMethod(Delegate method);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for a binding linked to the specified method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <returns>The binding if found, null otherwise</returns>
|
||||||
|
IControllerMethodBinding ForMethod(MethodInfo method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Tapeti.Annotations;
|
using Tapeti.Annotations;
|
||||||
@ -18,7 +19,6 @@ namespace Tapeti.Connection
|
|||||||
private readonly IMessageSerializer messageSerializer;
|
private readonly IMessageSerializer messageSerializer;
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TapetiPublisher(ITapetiConfig config, Func<ITapetiClient> clientFactory)
|
public TapetiPublisher(ITapetiConfig config, Func<ITapetiClient> clientFactory)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
@ -37,6 +37,65 @@ namespace Tapeti.Connection
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task PublishRequest<TController, TRequest, TResponse>(TRequest message, Expression<Func<TController, Action<TResponse>>> responseMethodSelector) where TController : class
|
||||||
|
{
|
||||||
|
await PublishRequest(message, responseMethodSelector.Body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task PublishRequest<TController, TRequest, TResponse>(TRequest message, Expression<Func<TController, Func<TResponse, Task>>> responseMethodSelector) where TController : class
|
||||||
|
{
|
||||||
|
await PublishRequest(message, responseMethodSelector.Body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task PublishRequest(object message, Expression responseMethodBody)
|
||||||
|
{
|
||||||
|
var callExpression = (responseMethodBody as UnaryExpression)?.Operand as MethodCallExpression;
|
||||||
|
var targetMethodExpression = callExpression?.Object as ConstantExpression;
|
||||||
|
|
||||||
|
var responseHandler = targetMethodExpression?.Value as MethodInfo;
|
||||||
|
if (responseHandler == null)
|
||||||
|
throw new ArgumentException("Unable to determine the response method", nameof(responseMethodBody));
|
||||||
|
|
||||||
|
|
||||||
|
var requestAttribute = message.GetType().GetCustomAttribute<RequestAttribute>();
|
||||||
|
if (requestAttribute?.Response == null)
|
||||||
|
throw new ArgumentException($"Request message {message.GetType().Name} must be marked with the Request attribute and a valid Response type", nameof(message));
|
||||||
|
|
||||||
|
var binding = config.Bindings.ForMethod(responseHandler);
|
||||||
|
if (binding == null)
|
||||||
|
throw new ArgumentException("responseHandler must be a registered message handler", nameof(responseHandler));
|
||||||
|
|
||||||
|
if (!binding.Accept(requestAttribute.Response))
|
||||||
|
throw new ArgumentException($"responseHandler must accept message of type {requestAttribute.Response}", nameof(responseHandler));
|
||||||
|
|
||||||
|
var responseHandleAttribute = binding.Method.GetCustomAttribute<ResponseHandlerAttribute>();
|
||||||
|
if (responseHandleAttribute == null)
|
||||||
|
throw new ArgumentException("responseHandler must be marked with the ResponseHandler attribute", nameof(responseHandler));
|
||||||
|
|
||||||
|
if (binding.QueueName == null)
|
||||||
|
throw new ArgumentException("responseHandler is not yet subscribed to a queue, TapetiConnection.Subscribe must be called before starting a request", nameof(responseHandler));
|
||||||
|
|
||||||
|
|
||||||
|
var properties = new MessageProperties
|
||||||
|
{
|
||||||
|
ReplyTo = binding.QueueName
|
||||||
|
};
|
||||||
|
|
||||||
|
await Publish(message, properties, IsMandatory(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task SendToQueue(string queueName, object message)
|
||||||
|
{
|
||||||
|
await PublishDirect(message, queueName, null, IsMandatory(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task Publish(object message, IMessageProperties properties, bool mandatory)
|
public async Task Publish(object message, IMessageProperties properties, bool mandatory)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,6 @@ namespace Tapeti.Default
|
|||||||
public IBindingResult Result => result;
|
public IBindingResult Result => result;
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ControllerBindingContext(IEnumerable<ParameterInfo> parameters, ParameterInfo result)
|
public ControllerBindingContext(IEnumerable<ParameterInfo> parameters, ParameterInfo result)
|
||||||
{
|
{
|
||||||
this.parameters = parameters.Select(parameter => new ControllerBindingParameter(parameter)).ToList();
|
this.parameters = parameters.Select(parameter => new ControllerBindingParameter(parameter)).ToList();
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Tapeti.Config;
|
using Tapeti.Config;
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
@ -15,6 +17,40 @@ namespace Tapeti
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">The message to send</param>
|
/// <param name="message">The message to send</param>
|
||||||
Task Publish(object message);
|
Task Publish(object message);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Publish the specified request message and handle the response with the controller method as specified
|
||||||
|
/// by the responseMethodSelector expression. The response method or controller must have a valid queue attribute.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The response method is called on a new instance of the controller, as is the case with a regular message.
|
||||||
|
/// To preserve state, use the Tapeti.Flow extension instead.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="responseMethodSelector">An expression defining the method which handles the response. Example: c => c.HandleResponse</param>
|
||||||
|
/// <param name="message">The message to send</param>
|
||||||
|
Task PublishRequest<TController, TRequest, TResponse>(TRequest message, Expression<Func<TController, Action<TResponse>>> responseMethodSelector) where TController : class;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Publish the specified request message and handle the response with the controller method as specified
|
||||||
|
/// by the responseMethodSelector expression. The response method or controller must have a valid queue attribute.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The response method is called on a new instance of the controller, as is the case with a regular message.
|
||||||
|
/// To preserve state, use the Tapeti.Flow extension instead.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="responseMethodSelector">An expression defining the method which handles the response. Example: c => c.HandleResponse</param>
|
||||||
|
/// <param name="message">The message to send</param>
|
||||||
|
Task PublishRequest<TController, TRequest, TResponse>(TRequest message, Expression<Func<TController, Func<TResponse, Task>>> responseMethodSelector) where TController : class;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a message directly to the specified queue. Not recommended for general use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queueName">The name of the queue to publish the message to</param>
|
||||||
|
/// <param name="message">The message to send</param>
|
||||||
|
Task SendToQueue(string queueName, object message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -299,6 +299,12 @@ namespace Tapeti
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IControllerMethodBinding ForMethod(MethodInfo method)
|
||||||
|
{
|
||||||
|
return methodLookup.TryGetValue(method, out var binding) ? binding : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Lock()
|
public void Lock()
|
||||||
{
|
{
|
||||||
methodLookup = this
|
methodLookup = this
|
||||||
|
@ -63,6 +63,10 @@ namespace Tapeti
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (method.GetCustomAttribute<ResponseHandlerAttribute>() != null)
|
||||||
|
context.SetBindingTargetMode(BindingTargetMode.Direct);
|
||||||
|
|
||||||
|
|
||||||
var allowBinding = false;
|
var allowBinding = false;
|
||||||
builderAccess.ApplyBindingMiddleware(context, () => { allowBinding = true; });
|
builderAccess.ApplyBindingMiddleware(context, () => { allowBinding = true; });
|
||||||
|
|
||||||
@ -97,7 +101,6 @@ namespace Tapeti
|
|||||||
MessageMiddleware = context.Middleware.Where(m => m is IControllerMessageMiddleware).Cast<IControllerMessageMiddleware>().ToList(),
|
MessageMiddleware = context.Middleware.Where(m => m is IControllerMessageMiddleware).Cast<IControllerMessageMiddleware>().ToList(),
|
||||||
CleanupMiddleware = context.Middleware.Where(m => m is IControllerCleanupMiddleware).Cast<IControllerCleanupMiddleware>().ToList()
|
CleanupMiddleware = context.Middleware.Where(m => m is IControllerCleanupMiddleware).Cast<IControllerCleanupMiddleware>().ToList()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
@ -46,7 +46,7 @@ The start method can have any name, but must be annotated with the ``[Start]`` a
|
|||||||
public DateTime RequestStart { get; set; }
|
public DateTime RequestStart { get; set; }
|
||||||
|
|
||||||
[Start]
|
[Start]
|
||||||
IYieldPoint StartFlow()
|
public IYieldPoint StartFlow()
|
||||||
{
|
{
|
||||||
RequestStart = DateTime.UtcNow();
|
RequestStart = DateTime.UtcNow();
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ Often you'll want to pass some initial information to the flow. The Start method
|
|||||||
public DateTime RequestStart { get; set; }
|
public DateTime RequestStart { get; set; }
|
||||||
|
|
||||||
[Start]
|
[Start]
|
||||||
IYieldPoint StartFlow(string colorFilter)
|
public IYieldPoint StartFlow(string colorFilter)
|
||||||
{
|
{
|
||||||
RequestStart = DateTime.UtcNow();
|
RequestStart = DateTime.UtcNow();
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ If the response handler is not asynchronous, use ``YieldWithRequestSync`` instea
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Start]
|
[Start]
|
||||||
IYieldPoint StartFlow(string colorFilter)
|
public IYieldPoint StartFlow(string colorFilter)
|
||||||
{
|
{
|
||||||
RequestStart = DateTime.UtcNow();
|
RequestStart = DateTime.UtcNow();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user