Cannot pass arguments to a BotCallback inside a ContinueConversationAsync method - c#

I am trying to implement proactive messages on bot framework v4, It works, but only with the string on the BotCallback function, I need to pass custom text but ContinueConversationAsync doesnt seems to allow it
public async Task<bool> SendProactiveMessage(MensajeExterno mensajeExterno)
{
var referenciaDeConversacion = ObtenerReferenciaConversacion(mensajeExterno.ConversationId);
var continuationActivity = referenciaDeConversacion.GetContinuationActivity();
if (referenciaDeConversacion == null) return false;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, referenciaDeConversacion, BotCallback, default(CancellationToken));
return true;
}
private ConversationReference ObtenerReferenciaConversacion(string conversationId)
{
return new ConversationReferenceModulo().ObtenerConversationReference(conversationId);
}
public MensajeroDefaultModulo(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
{
_adapter = adapter;
_appId = configuration["MicrosoftAppId"];
if (string.IsNullOrEmpty(_appId))
{
_appId = Guid.NewGuid().ToString(); //if no AppId, use a random Guid
}
}
private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
await turnContext.SendActivityAsync("proactive hello", cancellationToken: cancellationToken);
}

You can make a wrapper around BotCallback using LINQ approach. But personally i do not understand the idea of such delegates without any EventArgs.
[HttpPost]
public async Task<IActionResult> Post([FromBody] string messageText)
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) => await BotCallback(messageText, context, token), default(CancellationToken));
}
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
private async Task BotCallback(string message, ITurnContext turnContext, CancellationToken cancellationToken)
{
await turnContext.SendActivityAsync(message, cancellationToken: cancellationToken);
}

What I understand you are wanting to do is to pass a value to the BotCallback that can be sent back via the method SendActivityAsync.
To do this you can use a lambda expression instead of calling the BotCallback.
public async Task<bool> SendProactiveMessage(MensajeExterno mensajeExterno)
{
var yourVariable = "blah";
var referenciaDeConversacion = ObtenerReferenciaConversacion(mensajeExterno.ConversationId);
var continuationActivity = referenciaDeConversacion.GetContinuationActivity();
if (referenciaDeConversacion == null) return false;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, referenciaDeConversacion, async (context, token) => {
await turnContext.SendActivityAsync("proactive hello " + yourVariable, cancellationToken: cancellationToken);
}, default(CancellationToken));
return true;
}
This comes from this reference to a proactive messaging sample here:
https://github.com/microsoft/botbuilder-dotnet/issues/787
For Lambda expressions see Lambda Expressions (C# Programming Guide)
A lambda expression is an anonymous function that can contain
expressions and statements, and can be used to create delegates or
expression tree types.
All lambda expressions use the lambda operator =>, which is read as
"goes to". The left side of the lambda operator specifies the input
parameters (if any) and the right side holds the expression or
statement block. The lambda expression x => x * x is read "x goes to x
times x."
Updated with API controller example
The SendProactiveMessage needs to be in a Controller in your Bot project, for example:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
namespace ProactiveBot.Controllers
{
[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;
public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary<string, ConversationReference> conversationReferences)
{
_adapter = adapter;
_conversationReferences = conversationReferences;
_appId = configuration["MicrosoftAppId"];
// If the channel is the Emulator, and authentication is not in use,
// the AppId will be null. We generate a random AppId for this case only.
// This is not required for production, since the AppId will have a value.
if (string.IsNullOrEmpty(_appId))
{
_appId = Guid.NewGuid().ToString(); //if no AppId, use a random Guid
}
}
public async Task<IActionResult> Get([FromQuery(Name = "taskID")] int taskID)
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) => {
await context.SendActivityAsync("proactive task notification for TaskID: " + taskID.ToString());
}, default(CancellationToken));
}
// Let the caller know proactive messages have been sent
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1><p>" + taskID.ToString() + "</p></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
}
}
You can then get access to the context and token through the injected adapter. The above code is largely from the current Proactive Message sample code
So I can then call this API eg. http://localhost:3988/api/notify?taskID=5678 passing in the taskID parameter (in my case) which is then sent through to the user via ContinueConversationAsync.

Related

Sending request reponse message on Artemis using C#

I am trying to implement a request response pattern in C# with the ArtemisNetClient, but having a bit of trouble finding out how to do so in a more generic way in a real solution.
I was able to do something like this in two console applications based on some Java examples:
Sender
static async System.Threading.Tasks.Task Main(string[] args)
{
var connectionFactory = new ConnectionFactory();
var endpoint = Endpoint.Create("localhost", 5672, "guest", "guest");
var connection = await connectionFactory.CreateAsync(endpoint);
string guid = new Guid().ToString();
var requestAddress = "TRADE REQ1";
var responseAddress = "TRADE RESP";
Message message = new Message("BUY AMD 1000 SHARES");
message.SetCorrelationId(guid);
message.ReplyTo = responseAddress;
var producer = await connection.CreateProducerAsync(requestAddress, RoutingType.Anycast);
await producer.SendAsync(message);
var consumer = await connection.CreateConsumerAsync(responseAddress, RoutingType.Anycast);
var responseMessage = await consumer.ReceiveAsync();
Console.WriteLine(responseMessage.GetBody<string>());
}
Receiver
static async System.Threading.Tasks.Task Main(string[] args)
{
// Create connection
var connectionFactory = new ConnectionFactory();
var endpoint = Endpoint.Create("localhost", 5672, "guest", "guest");
var connection = await connectionFactory.CreateAsync(endpoint);
var requestAddress = "TRADE REQ1";
// Create consumer to receive trade request messages
var consumer = await connection.CreateConsumerAsync(requestAddress, RoutingType.Anycast);
var message = await consumer.ReceiveAsync();
Console.WriteLine($"Received message: {message.GetBody<string>()}");
// Confirm trade request and ssend response message
if (!string.IsNullOrEmpty(message.ReplyTo))
{
Message responseMessage = new Message("Confirmed trade request");
responseMessage.SetCorrelationId(message.CorrelationId);
var producer = await connection.CreateProducerAsync(message.ReplyTo);
await producer.SendAsync(responseMessage);
}
}
This worked as expected, but I'd like to have something more down the line of what is described in this article, except it doesn't have any examples of a request response pattern.
To elaborate, I currently have two services that I want to communicate across.
In service 1 I want to create and publish a message and then wait for a response to enrich the instance object and save it to a database. I currently have this, but it lacks the await response message.
public async Task<Instance> CreateInstance(Instance instance)
{
await _instanceCollection.InsertOneAsync(instance);
var #event = new InstanceCreated
{
Id = instance.Id,
SiteUrl = instance.SiteUrl
};
await _messageProducer.PublishAsync(#event);
return instance;
}
I figured I might need to setup a temporary queue/connection or something in the PublishAsync() and change it to e.g. Task<Message> to support returning a response message. But how would I go about doing that? Would I have to do a new connectionfactory + CreateConsumerAsync etc. like in the console app example?
public class MessageProducer
{
private readonly IAnonymousProducer _producer;
public MessageProducer(IAnonymousProducer producer)
{
_producer = producer;
}
public async Task PublishAsync<T>(T message, string replyTo = null, string correlationId = null)
{
var serialized = JsonSerializer.Serialize(message);
var address = typeof(T).Name;
var msg = new Message(serialized);
if (replyTo != null && correlationId != null)
{
msg.CorrelationId = correlationId;
msg.ReplyTo = replyTo;
}
await _producer.SendAsync(address, msg);
}
public async Task PublishAsync<T>(T message, string routeName, string replyTo = null, string correlationId = null)
{
var serialized = JsonSerializer.Serialize(message);
var address = routeName;
var msg = new Message(serialized);
if(replyTo != null && correlationId != null)
{
msg.CorrelationId = correlationId;
msg.ReplyTo = replyTo;
}
await _producer.SendAsync(address, msg);
}
}
In Service 2 I have a InstanceCreatedConsumer which receives messages, but again it lacks a way to return response messages.
public class InstanceCreatedConsumer : ITypedConsumer<InstanceCreated>
{
private readonly MessageProducer _messageProducer;
public InstanceCreatedConsumer(MessageProducer messageProducer)
{
_messageProducer = messageProducer;
}
public async Task ConsumeAsync(InstanceCreated message, CancellationToken cancellationToken)
{
// consume message and return response
}
}
I figured I might be able to extend the ActiveMqExtensions class with a ConsumeAsync and HandleMessage that handles the response message with a return value, but I haven't gotten as far yet.
public static IActiveMqBuilder AddTypedConsumer<TMessage, TConsumer>(this IActiveMqBuilder builder,
RoutingType routingType)
where TConsumer : class, ITypedConsumer<TMessage>
{
builder.Services.TryAddScoped<TConsumer>();
builder.AddConsumer(typeof(TMessage).Name, routingType, HandleMessage<TMessage, TConsumer>);
return builder;
}
private static async Task HandleMessage<TMessage, TConsumer>(Message message, IConsumer consumer, IServiceProvider serviceProvider, CancellationToken token)
where TConsumer : class, ITypedConsumer<TMessage>
{
try
{
var msg = JsonConvert.DeserializeObject<TMessage>(message.GetBody<string>());
using var scope = serviceProvider.CreateScope();
var typedConsumer = scope.ServiceProvider.GetService<TConsumer>();
await typedConsumer.ConsumeAsync(msg, token);
await consumer.AcceptAsync(message);
}
catch(Exception ex)
{
// todo
}
}
Am I totally wrong in what I am trying to achieve here, or is it just not possible with the ArtemisNetClient?
Maybe someone has an example or can confirm whether I am down the right path, or maybe I should be using a different framework.
I am new to this kind of communication through messages like ActiveMQ Artemis, so any guidance is appreciated.
I don't see anything in the ArtemisNetClient that would simplify the request/response pattern from your application's point of view. One might expect something akin to JMS' QueueRequestor, but I don't see anything like that in the code, and I don't see anything like that listed in the documentation.
I recommend you simply do in your application what you did in your example (i.e. manually create the consumer & producer to deal with the responses on each end respectively). The only change I would recommend is to re-use connections so you create as few as possible. A connection pool would be ideal here.
For what it's worth, it looks to me like the first release of ArtemisNetClient was just 3 months ago and according to GitHub all but 2 of the commits to the code-base came from one developer. ArtemisNetClient may grow into a very successful C# client implementation, but at this point it seems relatively immature. Even if the existing code is high quality if there isn't a solid community around the client then chances are it won't have the support necessary to get timely bug fixes, new features, etc. Only time will tell.
With version 2.7.0 ArtemisNetClient introduces IRequestReplyClient interface that can be used to implement a request-response messaging pattern. With ArtemisNetClient.Extensions.DependencyInjection this may look as follows:
Client Side:
First you need to register your typed request-reply client in DI:
public void ConfigureServices(IServiceCollection services)
{
/*...*/
var endpoints = new[] { Endpoint.Create(host: "localhost", port: 5672, "guest", "guest") };
services.AddActiveMq("bookstore-cluster", endpoints)
.AddRequestReplyClient<MyRequestReplyClient>();
/*...*/
}
MyRequestReplyClient is your custom class that expects the IRequestReplyClient to be injected via the constructor. Once you have your custom class, you can either expose the IRequestReplyClient directly or encapsulate sending logic inside of it:
public class MyRequestReplyClient
{
private readonly IRequestReplyClient _requestReplyClient;
public MyRequestReplyClient(IRequestReplyClient requestReplyClient)
{
_requestReplyClient = requestReplyClient;
}
public async Task<TResponse> SendAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken)
{
var serialized = JsonSerializer.Serialize(request);
var address = typeof(TRequest).Name;
var msg = new Message(serialized);
var response = await _requestReplyClient.SendAsync(address, msg, cancellationToken);
return JsonSerializer.Deserialize<TResponse>(response.GetBody<string>());
}
}
That's it regarding the client-side.
Worker side
To implement the worker side you can (as you suggested), change the ITypedConsumer interface to return the message that would be sent back, or you can provide the additional data (ReplyTo and CorrelationId headers) so you can send the response back as part of your consumer logic. I prefer the latter as it's a more flexible option in my opinion.
Modified ITypedConsumer might look like that:
public interface ITypedConsumer<in T>
{
public Task ConsumeAsync(T message, MessageContext context, CancellationToken cancellationToken);
}
Where MessageContext is just a simple dto:
public class MessageContext
{
public string ReplyTo { get; init; }
public string CorrelationId { get; init; }
}
HandleMessage extension method:
private static async Task HandleMessage<TMessage, TConsumer>(Message message, IConsumer consumer, IServiceProvider serviceProvider, CancellationToken token)
where TConsumer : class, ITypedConsumer<TMessage>
{
var msg = JsonSerializer.Deserialize<TMessage>(message.GetBody<string>());
using var scope = serviceProvider.CreateScope();
var typedConsumer = scope.ServiceProvider.GetService<TConsumer>();
var messageContext = new MessageContext
{
ReplyTo = message.ReplyTo,
CorrelationId = message.CorrelationId
};
await typedConsumer.ConsumeAsync(msg, messageContext, token);
await consumer.AcceptAsync(message);
}
MessageProducer has to be slightly changed as well, so you can explicitly pass address and CorrelationId:
public class MessageProducer
{
private readonly IAnonymousProducer _producer;
public MessageProducer(IAnonymousProducer producer)
{
_producer = producer;
}
public async Task PublishAsync<T>(string address, T message, MessageContext context, CancellationToken cancellationToken)
{
var serialized = JsonSerializer.Serialize(message);
var msg = new Message(serialized);
if (!string.IsNullOrEmpty(context.CorrelationId))
{
msg.CorrelationId = context.CorrelationId;
}
await _producer.SendAsync(address, msg, cancellationToken);
}
}
And finally, the exemplary consumer could work like that:
public class CreateBookConsumer : ITypedConsumer<CreateBook>
{
private readonly MessageProducer _messageProducer;
public CreateBookConsumer(MessageProducer messageProducer)
{
_messageProducer = messageProducer;
}
public async Task ConsumeAsync(CreateBook message, MessageContext context, CancellationToken cancellationToken)
{
var #event = new BookCreated
{
Id = Guid.NewGuid(),
Title = message.Title,
Author = message.Author,
Cost = message.Cost,
InventoryAmount = message.InventoryAmount,
UserId = message.UserId,
Timestamp = DateTime.UtcNow
};
await _messageProducer.PublishAsync(context.ReplyTo, #event, new MessageContext
{
CorrelationId = context.CorrelationId
}, cancellationToken);
}
}

How to call API in Discord.NET command?

I have problem with calling API in my Discord Bot, I am trying to learn async, await but it seems as I am stuck.
This is my Call API class where I am calling API.
public async Task<Root> GetInfoAsync()
{
string path = "https://onemocneni-aktualne.mzcr.cz/api/v2/covid-19/zakladni-prehled.json";
Root data = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<Root>(json);
}
return data;
}
public async Task<string> VypisAsync()
{
Root data = await this.GetInfoAsync();
int deaths = data.data[0].umrti,
recovered = data.data[0].vyleceni,
positive = data.data[0].aktivni_pripady,
hospitalized = data.data[0].aktualne_hospitalizovani;
return $"Aktualni situace v ČR:\n" +
$"vyleceni: {recovered}\n" +
$"aktualne nemocni: {positive}\n" +
$"hospitalizovani: {hospitalized}\n" +
$"zemreli: {deaths}";
}
And here is my covid command
public class CovidModule : ModuleBase<SocketCommandContext>
{
// ~void -> current covid situation in CZ
[Command("covid")]
[Summary("shows current covid situation in CZ")]
public async Task<Task> CovidAsync()
{
CovidApi api = new CovidApi();
return ReplyAsync(await api.VypisAsync());
}
}
I know that all others commands return Task, but I don't know how to make it like that.
ReplyAsync() is an async method, so you need to await it.
Your CovidAsync() method isn't returning an actual value, so in the synchronous world its return value would be void. Since it's an async method you return Task instead.
public async Task CovidAsync()
{
CovidApi api = new CovidApi();
await ReplyAsync(await api.VypisAsync());
}
As an aside, it would be better to have CovidApi as a member of your CovidModule. That way you don't need to keep newing it up in each method.

How to pass in options to a Dialog when using TestFlow - Azure botframework v4 - C# - Unit Tests

Using Azure Bot framework v4 - C# ... xunit...
I have the following unit test for testing a Component Dialog that in turn uses an AdaptiveDialog.
As part of unit testing the dialog, I want to pass in options to the dialog - the options that get passed to the OnBeginDialogAsync overload of the Dialog class.
Any thoughts on how we can pass myDialogOptions as the options to the dialog?
Thank you
Regards
Athadu
public class ConfirmationDialog : ComponentDialog
{
public class Options
{
public string PromptTemplate { get; set; }
}
public ConfirmationDialog()
: base("test")
{
}
protected override Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default)
{
//
// Avoiding using BotState here. Instead ... use options...
// do something based on passed in options
//
}
}
[Fact]
public async Task TestMyComponentDialogThatUsesAdaptiveDialog()
{
//Arrange
Setup();
TestAdapter = (TestAdapter)new TestAdapter("my")
.UseStorage(memoryStorage)
.UseBotState(UserState, ConversationState)
.Use(Middlewares[0]);
var dialogState = ConversationState.CreateProperty<DialogState>("dialogState");
var dialogToTest = new ConfirmationDialog();
var dialogManager = new DialogManager(dialogToTest);
var myDialogOptions = new MyOptions { Name = "Jon Doe" };
await new TestFlow(TestAdapter, async (turnContext, cancellationToken) =>
{
<<<<<< How to pass in Dialog Options myDialogOptions to the dialog - need to access it within OnBeginDialogAsync >>>>>
<<<<<< of Dialog class override method OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default) >>>>>
var result = await dialogManager.OnTurnAsync(turnContext, cancellationToken);
})
//Act
.SendConversationUpdate()
//Assert
.AssertReply(activity =>
{
var resolvedActivity = activity.AsMessageActivity();
resolvedActivity.Text.Should().StartWith("Some Text");
}, null, 2100)
.StartTestAsync();
}
You can see in the source code that dialog managers don't pass any options to their root dialogs:
private async Task<DialogTurnResult> HandleBotOnTurnAsync(DialogContext dc, CancellationToken cancellationToken)
{
DialogTurnResult turnResult;
// the bot is running as a root bot.
if (dc.ActiveDialog == null)
{
// start root dialog
turnResult = await dc.BeginDialogAsync(_rootDialogId, cancellationToken: cancellationToken).ConfigureAwait(false);
}
else
{
// Continue execution
// - This will apply any queued up interruptions and execute the current/next step(s).
turnResult = await dc.ContinueDialogAsync(cancellationToken).ConfigureAwait(false);
if (turnResult.Status == DialogTurnStatus.Empty)
{
// restart root dialog
turnResult = await dc.BeginDialogAsync(_rootDialogId, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
await SendStateSnapshotTraceAsync(dc, "Bot State", cancellationToken).ConfigureAwait(false);
return turnResult;
}
If you want to pass options to BeginDialogAsync then you should call that or PromptAsync yourself.

await operator giving issue when using with lambda

I have 2 Entity and I want to copy some data from Second Entity to First Entity and after that I want to return a simple string saying Success.I am using Polly to make http request.I am planning to get data in json and then convert it in my Entity model and do the manipulation which I am able to do but Calling both the task which return differnt types(can be slight different data model) giving some error.I am not so good in Multithreading approach.
public interface IMyRepository
{
string ValidateData(MyData myData);
}
public class MyRepository :IMyRepository
{ private readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;
public MyRepository()
{
_httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
r => r.StatusCode == HttpStatusCode.InternalServerError)
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(retryAttempt), (exception, timeSpan, retryCount, context1) =>
{
var msg = $"Retry {retryCount} implemented with Pollys RetryPolicy " +
$"of {context1.PolicyKey} " +
$"at {context1.ExecutionKey}, " +
$"due to: {exception}.";
});
}
public string ValidateData(MyData MyData)
{
var MyDataOne= Task<MyData>.Factory.StartNew(() => await MyRepository.getProfileOne());
var MyDataTwo= Task<MyData>.Factory.StartNew(() => await MyRepository.getProfileTwo());
//Update some property of MyDataOne on basis of MyDataTwo and return true or fasle in variable **result**
return result;
}
public static async Task<InsuranceCompanyData> getCusomerProfile()
{
var httpClient = GetHttpClient();
string requestEndpoint = "numbers/Get";
HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));
IEnumerable<int> numbers = await httpResponse.Content.ReadAsAsync<IEnumerable<int>>();
return new InsuranceCompanyData();
}
private static HttpClient GetHttpClient()
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"http://localhost:2351/api/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}
}
public static async Task<MyData> getProfileOne()
{
var httpClient = GetHttpClient();
string requestEndpoint = "/numbers/Get1";
HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));
// IEnumerable<string> data1= await httpResponse.Content.ReadAsAsync<IEnumerable<string>>();
return new MyData();
}
public static async Task<MyData> getProfileTwo()
{
var httpClient = GetHttpClient();
string requestEndpoint = "/numbers/Get2";
HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));
// IEnumerable<string> data2= await httpResponse.Content.ReadAsAsync<IEnumerable<string>>();
return new MyyData();
}
private static HttpClient GetHttpClient()
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"http://localhost:2351/api/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}
I get these errors:
The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.
And
An object reference is required for the non-static field, method, or property 'MyRepository._httpRequestPolicy'
Instead of using Task.Factory.StartNew which is not recommended and doesn't support async lambdas (which you don't even need here), use Task.Run:
var profileOneTask = Task.Run(() => getProfileOne());
var profileTwoTask = Task.Run(() => getProfileTwo());
Notice that I changed the variable names to reflect what they actually are. They are tasks that may, at some point, have a result. They are not the result of those operations.
For the second problem, you declared _httpRequestPolicy as an instance member when you should have declared it as a static member for it to be usable without an instance. As discussed in the comments, though, you could just make getProfileOne and getProfileTwo instance methods.
Why you don't change ValidateData signature and add async keyword to method ?
public async Task<string> ValidateDataAsync(MyData MyData)
{
var task1 = Task<MyData>.Factory.StartNew(() => MyRepository.getProfileOne());
var task2 = Task<MyData>.Factory.StartNew(() => MyRepository.getProfileTwo());
await Task.WhenAll(task1, task2)
//Update some property of MyDataOne on basis of MyDataTwo and return true or fasle in variable **result**
return result;
}
As #Camilo Terevinto said it's better to use Task.Run instead of TaskFactory.StartNew.
Task.Run vs Task.Factory.StartNew

How to pass the parameter from controller to FormDialog state model

Requirement
FormStateModel already contains FIRST input that users types.
Code
Simply I want to put the string that is in activity.Text inside FormStateModel:
private IDialog<FormStateModel> MakeRootDialog(string input)
{
return Chain.From(() => new FormDialog<FormStateModel>(
new FormStateModel() { Question = input },
ContactDetailsForm.BuildForm,
FormOptions.None));
}
=
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(
toBot: activity,
MakeRoot: () => this.MakeRootDialog(activity.Text));
}
else
{
await HandleSystemMessageAsync(activity);
}
var response = this.Request.CreateResponse(HttpStatusCode.OK);
return response;
}
On ConversationUpdate I start conversation simply by asking "Please type your Question:"
private static async Task<Activity> HandleSystemMessageAsync(Activity message)
{
switch (message.Type)
{
case ActivityTypes.DeleteUserData:
break;
case ActivityTypes.ConversationUpdate:
await Welcome(message);
break;
(...)
In that way:
private static async Task Welcome(Activity activity)
{
(...)
reply.Text = string.Format("Hello, how can we help you today? Please type your Question:");
await client.Conversations.ReplyToActivityAsync(reply);
(...)
}
But I can not find a way how to pass it. In this case this exception occurs:
anonymous method closures that capture the environment are not serializable, consider removing environment capture or using a reflection serialization surrogate:
Is there any way around that to populate state model at this step?
Solved by calling RootDialog inside MessagesController, then Calling new FormDialog by context.Call(form, (...));
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
await Conversation.SendAsync(activity, () => new LayerDialog());
}
LayerDialog:
[Serializable]
public class LayerDialog: IDialog<IMessageActivity>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(this.OnMessageReceivedAsync);
}
private async Task OnMessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var awaited = await result;
FormStateModel model = new FormStateModel();
model.Value = awaited.Text;
var form = new FormDialog<FormStateModel >(model ,
BuildForm , FormOptions.PromptInStart);
context.Call(form , this.AfterResume);
}

Categories