How do I save result in string of executed command - c#

As the title says how can I save in string the result of the executed command?
SendCommand("server.hostname");
My code:
public void SendCommand(string command)
{
PacketModel packet = new PacketModel()
{
Identifier = 1,
Message = command,
Name = "RustManager"
};
string packetString = JsonConvert.SerializeObject(packet);
_webSocket.SendAsync(packetString, null);
}
public void GetServerHostname()
{
SendCommand("server.hostname");
}

Due to my small reputation I cannot comment - which is what I would have done before that.
Normally methods that end on Async are async and return a Task<T> type.
Using the await keyword makes your method async which is why you have to mark it as async in the method head.
Link to C#-Documentation on the await keyword
It is really hard to say how to get your code running since I don't have alot of information but maybe this helps:
public async void SendCommand(string command)
{
PacketModel packet = new PacketModel()
{
Identifier = 1,
Message = command,
Name = "RustManager"
};
string packetString = JsonConvert.SerializeObject(packet);
var result = await _webSocket.SendAsync(packetString, null);
}
EDIT 1:
After getting some new information here is my new answer:
You use this class for your websocket. If you look at the signiture of the "SendAsync" method you can see, that it returns void (which means "nothing"). So you will not be able to "Store some kind of information" here.
The method looks like this:
public void SendAsync (string data, Action<bool> completed)
{ [...] }
You will have to listen to the WebSocket and wait for a server-side response. It seems, that the library supports that via events:
ws.OnMessage += (sender, e) => {
...
};
So you can define an eventhandler to process the server-response.
If you would like to get the message data, you should access e.Data or e.RawData property.
e.Data property returns a string, so it is mainly used to get the text message data.
(source (GitHub Readme))
So to fullfil your wishes try the following:
1.) At initialization of your _websocket instance subscribe to the .OnMessageevent with a corresponding event handler. (Some information about that)
2.) Send your message as you do it now with SendAsync
3.) If your server responds with a message to the network socket, the OnMessageevent will fire and you will be able to get the Data from the Eventargument e
(I did not test this - but it should work since it is used this way in the examples)

Related

Unable to return a value from SignalR Client from a different method

I'm working on a Winforms app that executes SQL Procedures through a SignalR client. I'm relatively new to using SignalR and am still wrapping my head around it.
I start off by running my connection method to establish a connection with my SignalR service. I have two addresses configured ready for when I puslish but the DEV configuration leads to the SignalR service I am hosting locally.
Connection to SignalR (ConnectHub)
private async Task ConnectHub()
{
string hubAddress = "";
#if DEBUG
HubAddress = ConfigurationManager.AppSettings["HubAddress_DEV"];
#else
HubAddress = ConfigurationManager.AppSettings["HubAddress_PROD"];
#endif
if (string.IsNullOrEmpty(hubAddress))
{
MessageBox.Show("Hub Address is missing from configuration.");
}
ConnectionHandler.Client = new HubClient(hubAddress, "MyHub");
ConnectionHandler.Client.MyAlert += ConnectionHandler.ClientOnMyAlert;
ConnectionHandler.Client.ServerErrorEvent += ConnectionHandler.ClientOnServerErrorEvent;
await ConnectionHandler.Client.Connect(new List<string>() {
VehicleInfo.ThisVehicle.WarehouseCode,
VehicleInfo.ThisVehicle.VehicleName
});
}
My client is stored globally in my ConnectionHandler class where my event handlers are also kept. (I have breakpoints on these as I have not implemented them yet)
ConnectionHandler Class
public static class ConnectionHandler
{
public static HubClient Client { get; set; }
public static void ClientOnServerErrorEvent(string error)
{
throw new NotImplementedException(); //Currently not implemented
}
public static async Task ClientOnMyAlert(EnumMyAlertType alerttype, string message, Exception exception)
{
await Task.Yield(); //Currently not implemented
}
}
When I call the code to Invoke the procedure in my SignalR client, it returns a DataTable to me which is the intended result.
Call to SignalR
await ConnectHub();
DataTable dt = await ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>(
"FetchStatuses",
new object[0]); //This call works as intended and returns a populated DataTable
StatusInfo = new CStatuses();
All the above code is currently done on the main form, however I wanted to move this call to SignalR into a constructor to try and tidy things up.
The problem comes when I try to move this call into another method, the program hangs as I don't think it has received the return value from SignalR, I have placed a breakpoint beneath it and it is not reached. A TryCatch reveals nothing as it hangs within the "Try" with no exception.
Calling from contructor
public CStatuses()
{
Statuses = new List<CStatus>();
var dataTable = ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>("FetchStatuses",
new object[0])
.Result; //My program hangs on this line and proceeds no further
I am at a loss as to why it is doing this when I can get a value from the client from the form and when other members of my team have tried to do the same thing they can make a call to SignalR also from a different method.
Does anyone have any ideas as to how I can make this work?
I realize this has gotten quite long but if I can elaborate on things please let me know
FIXED CODE THANKS TO SOLUTION:
I have moved the code from my CStatuses constructor into a new async method within the same class and called it after initialization. This removes the need for .Result and appears to solve the problem for me.
public async Task PopulateStatuses()
{
var dataTable = await ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>("FetchStatuses",
new object[0]);
Statuses = new List<CStatus>();
foreach (DataRow row in dataTable.Rows)
{
var status = new CStatus
{
StatusId = Common.Utility.GetInt16Value(row["StatusID"]),
StatusCode = Common.Utility.GetStringValue(row["StatusCode"]),
Description = Common.Utility.GetStringValue(row["Description"])
};
Statuses.Add(status);
}
}
You are running into a deadlock with the .Result call, I would suggest creating an async method in the CStatuses class and after you initialize your CStatuses class call the websocket for data.

Why is the hub context connection ID inaccessible at runtime in my VERY simple method?

Let me start by saying this all works perfectly at the moment except for one thing - the notification update for the progress bar goes to all clients (as you would expect given that the code example I used sends to ...Clients.All).
All I want to do is send the notification back to the client that initiated the current call to the hub. That's it, nothing else. There is no concept of "logging in" on this website so there's no user identity stuff to work with.
My method is:
public void NotifyUpdates(decimal val)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<EventsForceHub>();
if (hubContext != null)
{
//"updateProgress" is javascript event trigger name
await hubContext.Clients.All.updateProgress(val);
}
}
So back in my view I subscribe to "updateProgress" and it works fine - the progress bar updates as desired. But if another client happens to be connected to the hub, when one runs the async task and causes the NotifyUpdates method to run, then ALL connected clients see the taskbar update, which is a bit confusing for them!
In debug, if I inspect hubContext at runtime I can see a Clients property and that has a Connection property which has an Identity property with a unique GUID. Perfect! Just what I want to use. But... I cannot access it! If I try:
var currentConnection = hubContext.Clients.Connection;
...then I simply get a
"does not contain a definition for 'Connection'"
error, which I simply don't understand.
I've tried accessing Context.ConnectionId from the method too, but Context is null at that point so I'm a bit confused. The server method that uses NotifyUpdates to send information back to the client is called via a normal asp.net button, not via AJAX.
Clarification on structure
I think there is a degree of confusion here. It's a very simply webpage with an asp.net button control on it. The eventhandler for that button invokes an async method to return data from the server via a service call/repository.
Inside the async method, it has to process each returned data line and make three or four remote web api calls. On each loop I make a call back to the NotifyUpdates SignalR method shown above with a percentage complete number so this can update back to the client via an eventhandler for the method name specified (updateProgress as shown above). There could be dozens of data lines and each data line requires several Web API calls to a remote server to add data. This can take several seconds per iteration, hence me sending back the "update progress" to the client via that updateProgress method call.
NEW ANSWER
Based on your comments I made the following little test:
It will allow clients to connect to the hub with a clientName, and every client is listening to updates send to them. We will have a group defined for them to be able to notify them from the server side.
I made a dummy progress simulator class to throw some update values to the users.
The code:
Hub class:
public class EventsForceHub : Hub {
public static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<EventsForceHub>();
// allow users to join to hub and get s dedicated group/channel for them, so we can update them
public async Task JoinGroup(string clientName) {
string clientID = Context.ConnectionId;
ClientInfo.clients.Add(clientID, new MyAppClient(clientID, clientName));
await Groups.Add(clientID, clientName);
// this is just mockup to simulate progress events (this uis not needed in real application)
MockupProgressGenerator.DoJob(clientName, 0);
}
public static void NotifyUpdates(decimal val, string clientName) {
// update the given client on his group/channel
hubContext.Clients.Group(clientName).updateProgress(val);
}
}
Some little helper classes:
// client "storage"
public static class ClientInfo {
public static Dictionary<string, MyAppClient> clients = new Dictionary<string, MyAppClient>();
// .. further data and methods
}
// client type
public class MyAppClient {
public string Id { get; set; }
public string Name { get; set; }
// .. further prooerties and methods
public MyAppClient(string id, string name) {
Id = id;
Name = name;
}
}
// this a completely made up and dumb class to simulate slow process and give some simple progress events
public static class MockupProgressGenerator {
public static void DoJob(string clientName, int status) {
if (status < 100) {
Task.Delay(1000).ContinueWith(a =>
{
EventsForceHub.NotifyUpdates(status += 20, clientName);
DoJob(clientName, status);
});
}
}
}
Let's see two simple clients in JS:
$(function () {
var eventsForceHub = $.connection.eventsForceHub;
$.connection.hub.start().done(function () {
$('body').append("Joining with Name: Jerry");
eventsForceHub.server.joinGroup("Jerry");
});
eventsForceHub.client.updateProgress = function (val) {
// message received
$('body').append('<br>').append("New Progress message: " + val);
};
});
For simplicity, same code, with different params, I even put this in two different html pages and stated execution in slightly different timing.
$(function () {
var eventsForceHub = $.connection.eventsForceHub;
$.connection.hub.start().done(function () {
$('body').append("Joining with Name: Tom");
eventsForceHub.server.joinGroup("Tom");
});
eventsForceHub.client.updateProgress = function (val) {
// message received
$('body').append('<br>').append("New Progress message: " + val);
};
});
See it in action:
FIRST ANSWER
I made a small web application to verify your claim. You may create the following to be able to isolate the issue from other possible problems.
I created an empty Web Application and included SignalR.
This is the hub class:
public class EventsForceHub : Hub {
public void NotifyUpdates(decimal val) {
var hubContext = GlobalHost.ConnectionManager.GetHubContext<EventsForceHub>();
if (Context != null) {
string clientID = Context.ConnectionId; // <-- on debug: Ok has conn id.
object caller = Clients.Caller; // <-- on debug: Ok, not null
object caller2 = Clients.Client(clientID); // <-- on debug: Ok, not null
Clients.Caller.updateProgress(val); // Message sent
Clients.Client(clientID).updateProgress(val); // Message sent
}
if (hubContext != null) {
//"updateProgress" is javascript event trigger name
hubContext.Clients.All.updateProgress(val); // Message sent
}
}
}
This is the web page:
<script src="Scripts/jquery-1.10.2.min.js"></script>
<script src="Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var eventsForceHub = $.connection.eventsForceHub;
$.connection.hub.start().done(function () {
// send mock message on start
console.log("Sending mock message: " + 42);
eventsForceHub.server.notifyUpdates(42);
});
eventsForceHub.client.updateProgress = function (val) {
// message received
console.log("New Progress message: " + val);
};
});
</script>
Try to build an application as little as this to isolate the issue. I have not had any of the issues you mentioned.
For the sake of simplicity and using the debugger I took away the await and async.
Actually, SignalR will take care of that for you. You will get a new instance of your Hub class at every request, no need to force asynchrony into the methods.
Also, GlobalHost is defined as static which should be shared between instances of your Hub class. Using in an instance method does not seem like a very good idea. I think you want to use the Context and the Clients objects instead. However, while debugging we can verify that using GlobalHost also works.
Some debugger screenshots showing runtime values of callerId, Clients.Caller and Clients.Client(clientID):
Understanding SignalR better will help you a lot in achieving your goal.
Happy debugging!
If you want to send the notification back to the client
you should not call
hubContext.Clients.All.updateProgress(val);
instead try
accessing the current user's ConnectionId and use Clients.Client
hubContext.Clients.Client(Context.ConnectionId);

Synchronizing asynchronous events

I have a class that receives standard .Net events from an external class.
These events have an address property (in addition to a lot of other properties, of course) that I can use to synchronize my events, so that I should be able to create a method to Get something, wait for the correct event, then return the data from the event in the Get method.
However, I'm fairly new to synchronization in C# and was hoping any of you could help me out. Below is somewhat pseudo code for what I want to accomplish:
Someone calls DoAsynchronousToSynchronousCall
That method waits until an event have been received with the same address (or until it times out)
The event checks against all current requests. If it finds a request with the same address, let DoAsynchronousToSynchronousCall know the reply has arrived
DoAsynchronousCall gets (or retrieves) the reply and returns it to the caller
public class MyMessage
{
public string Address { get; set; }
public string Data { get; set; }
}
public Main
{
externalClass.MessageReceived += MessageReceived;
}
public void MessageReceived(MyMessage message)
{
MyMessage request = _requestQueue.FirstOrDefault(m => m.Address = message.Address);
if (request != null)
{
// Do something to let DoAsynchronousToSynchronousCall() know the reply has arrived
}
}
private List<MyMessage> _requestQueue = new List<MyMessage>();
public MyMessage DoAsynchronousToSynchronousCall(MyMessage message)
{
_requestQueue.Add(message);
externalClass.Send(message);
// Do something to wait for a reply (as checked for above)
MyMessage reply = WaitForCorrectReply(timeout: 10000);
return reply;
}
I feel like I'm missing an opportunity to use async and await (yet I don't know how), and I hope you're able to understand what I'm trying to accomplish based on the information above.
You really can't have multiple calls on the fly and have synchronous responses. If you want synchronous responses for multiple calls then you need to do the calls synchronously too.
I would look at using Microsoft's Reactive Extensions (NuGet "Rx-Main") to make what you're doing as simple as possible. Rx lets you turn events into streams of values that you can query against.
Here's what I would do.
I would first define a stream of the received messages as IObservable<MyMessage> receivedMessages like this:
receivedMessages =
Observable
.FromEvent<MessageReceivedHandler, MyMessage>(
h => externalClass.MessageReceived += h,
h => externalClass.MessageReceived -= h);
(You didn't provide a class def so I've called the event delegate MessageReceivedHandler.)
Now you can redefine DoAsynchronousToSynchronousCall as:
public IObservable<MyMessage> DoAsynchronousCall(MyMessage message)
{
return Observable.Create<MyMessage>(o =>
{
IObservable<MyMessage> result =
receivedMessages
.Where(m => m.Address == message.Address)
.Take(1);
IObservable<MyMessage> timeout =
Observable
.Timer(TimeSpan.FromSeconds(10.0))
.Select(x => (MyMessage)null);
IDisposable subscription =
Observable
.Amb(result, timeout)
.Subscribe(o);
externalClass.Send(message);
return subscription;
});
}
The result observable is the receivedMessages filtered for the current message.Address.
The timeout observable is a default value to return if the call takes longer than TimeSpan.FromSeconds(10.0) to complete.
Finally the subscription uses Observable.Amb(...) to determine which of result or timeout produces a value first and subscribes to that result.
So now to call this you can do this:
DoAsynchronousCall(new MyMessage() { Address = "Foo", Data = "Bar" })
.Subscribe(response => Console.WriteLine(response.Data));
So, if I make a simple definition of ExternalClass like this:
public class ExternalClass
{
public event MessageReceivedHandler MessageReceived;
public void Send(MyMessage message)
{
this.MessageReceived(new MyMessage()
{
Address = message.Address,
Data = message.Data + "!"
});
}
}
...I get the result Bar! printed on the console.
If you have a whole bunch of messages that you want to process you can do this:
var messagesToSend = new List<MyMessage>();
/* populate `messagesToSend` */
var query =
from message in messagesToSend.ToObservable()
from response in DoAsynchronousCall(message)
select new
{
message,
response
};
query
.Subscribe(x =>
{
/* Do something with each correctly paired
`x.message` & `x.response`
*/
});
You're probably looking for ManualResetEvent which functions as a "toggle" of sorts to switch between thread-blocking and non-blocking behavior. The DoAsynchronousToSynchronousCall would Reset and then WaitOne(int timeoutMilliseconds) the event to block the thread, and the thing checking for the correct reply arrived would do the Set call to let the thread continue on its way if the correct thing arrived.

what does callback mean? [duplicate]

What's a callback and how is it implemented in C#?
I just met you,
And this is crazy,
But here's my number (delegate),
So if something happens (event),
Call me, maybe (callback)?
In computer programming, a callback is executable code that is passed as an argument to other code.
—Wikipedia: Callback (computer science)
C# has delegates for that purpose. They are heavily used with events, as an event can automatically invoke a number of attached delegates (event handlers).
A callback is a function that will be called when a process is done executing a specific task.
The usage of a callback is usually in asynchronous logic.
To create a callback in C#, you need to store a function address inside a variable. This is achieved using a delegate or the new lambda semantic Func or Action.
public delegate void WorkCompletedCallBack(string result);
public void DoWork(WorkCompletedCallBack callback)
{
callback("Hello world");
}
public void Test()
{
WorkCompletedCallBack callback = TestCallBack; // Notice that I am referencing a method without its parameter
DoWork(callback);
}
public void TestCallBack(string result)
{
Console.WriteLine(result);
}
In today C#, this could be done using lambda like:
public void DoWork(Action<string> callback)
{
callback("Hello world");
}
public void Test()
{
DoWork((result) => Console.WriteLine(result));
DoWork(Console.WriteLine); // This also works
}
Definition
A callback is executable code that
is passed as an argument to other code.
Implementation
// Parent can Read
public class Parent
{
public string Read(){ /*reads here*/ };
}
// Child need Info
public class Child
{
private string information;
// declare a Delegate
delegate string GetInfo();
// use an instance of the declared Delegate
public GetInfo GetMeInformation;
public void ObtainInfo()
{
// Child will use the Parent capabilities via the Delegate
information = GetMeInformation();
}
}
Usage
Parent Peter = new Parent();
Child Johny = new Child();
// Tell Johny from where to obtain info
Johny.GetMeInformation = Peter.Read;
Johny.ObtainInfo(); // here Johny 'asks' Peter to read
Links
more details for C#.
A callback is a function pointer that you pass in to another function. The function you are calling will 'callback' (execute) the other function when it has completed.
Check out this link.
If you referring to ASP.Net callbacks:
In the default model for ASP.NET Web
pages, the user interacts with a page
and clicks a button or performs some
other action that results in a
postback. The page and its controls
are re-created, the page code runs on
the server, and a new version of the
page is rendered to the browser.
However, in some situations, it is
useful to run server code from the
client without performing a postback.
If the client script in the page is
maintaining some state information
(for example, local variable values),
posting the page and getting a new
copy of it destroys that state.
Additionally, page postbacks introduce
processing overhead that can decrease
performance and force the user to wait
for the page to be processed and
re-created.
To avoid losing client state and not
incur the processing overhead of a
server roundtrip, you can code an
ASP.NET Web page so that it can
perform client callbacks. In a client
callback, a client-script function
sends a request to an ASP.NET Web
page. The Web page runs a modified
version of its normal life cycle. The
page is initiated and its controls and
other members are created, and then a
specially marked method is invoked.
The method performs the processing
that you have coded and then returns a
value to the browser that can be read
by another client script function.
Throughout this process, the page is
live in the browser.
Source: http://msdn.microsoft.com/en-us/library/ms178208.aspx
If you are referring to callbacks in code:
Callbacks are often delegates to methods that are called when the specific operation has completed or performs a sub-action. You'll often find them in asynchronous operations. It is a programming principle that you can find in almost every coding language.
More info here: http://msdn.microsoft.com/en-us/library/ms173172.aspx
Dedication to LightStriker:
Sample Code:
class CallBackExample
{
public delegate void MyNumber();
public static void CallMeBack()
{
Console.WriteLine("He/She is calling you. Pick your phone!:)");
Console.Read();
}
public static void MetYourCrush(MyNumber number)
{
int j;
Console.WriteLine("is she/he interested 0/1?:");
var i = Console.ReadLine();
if (int.TryParse(i, out j))
{
var interested = (j == 0) ? false : true;
if (interested)//event
{
//call his/her number
number();
}
else
{
Console.WriteLine("Nothing happened! :(");
Console.Read();
}
}
}
static void Main(string[] args)
{
MyNumber number = Program.CallMeBack;
Console.WriteLine("You have just met your crush and given your number");
MetYourCrush(number);
Console.Read();
Console.Read();
}
}
Code Explanation:
I created the code to implement the funny explanation provided by LightStriker in the above one of the replies. We are passing delegate (number) to a method (MetYourCrush). If the Interested (event) occurs in the method (MetYourCrush) then it will call the delegate (number) which was holding the reference of CallMeBack method. So, the CallMeBack method will be called. Basically, we are passing delegate to call the callback method.
Please let me know if you have any questions.
Probably not the dictionary definition, but a callback usually refers to a function, which is external to a particular object, being stored and then called upon a specific event.
An example might be when a UI button is created, it stores a reference to a function which performs an action. The action is handled by a different part of the code but when the button is pressed, the callback is called and this invokes the action to perform.
C#, rather than use the term 'callback' uses 'events' and 'delegates' and you can find out more about delegates here.
callback work steps:
1) we have to implement ICallbackEventHandler Interface
2) Register the client script :
String cbReference = Page.ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context");
String callbackScript = "function UseCallBack(arg, context)" + "{ " + cbReference + ";}";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "UseCallBack", callbackScript, true);
1) from UI call Onclient click call javascript function for EX:- builpopup(p1,p2,p3...)
var finalfield= p1,p2,p3;
UseCallBack(finalfield, ""); data from the client passed to server side by using UseCallBack
2) public void RaiseCallbackEvent(string eventArgument) In eventArgument we get the passed data
//do some server side operation and passed to "callbackResult"
3) GetCallbackResult() // using this method data will be passed to client(ReceiveServerData() function) side
callbackResult
4) Get the data at client side:
ReceiveServerData(text) , in text server response , we wil get.
A callback is a function passed as an argument to another function. This technique allows a function to invoke the parameter function argument and even to pass a value back to the caller. A callback function can be designed to run before/after the function has finished and can pass a value.
It is a kind of construct where you call a long running function and ask him to call you back once it has finished with can return a parameter result to the caller.
It's like someone calls you in the middle of your work asking for status and you say "you know what give me 5 min and i will call you back" and at the end you call him to update. If you are a function the caller just added and passed another function that you invoked at the end. This can simpley be written in C# as:
public void VinodSrivastav(Action statusUpdate){
//i am still here working..working
//i have finished, calling you
statusUpdate();
}
//invokes
stackoverflow.VinodSrivastav((cam) => {
Console.Write("Is it finished");
});
The one simple example is the iterator function where the return will be multiple times, one can argue that we have yield for it:
public void IntreationLoop(int min, int max,Action<int> Callback)
{
for(int i = min;i<= max;i++)
Callback(i);
}
//call
IntreationLoop(5,50,(x) => { Console.Write(x); }); //will print 5-50 numbers
In the code above the function return type is void but it has an Action<int> callback which is called and sends each item from the loop to the caller.
The same thing can be done with if..else or try..catch block as:
public void TryCatch(Action tryFor,Action catchIt)
{
try{
tryFor();
}
catch(Exception ex)
{
Console.WriteLine($"[{ex.HResult}] {ex.Message}");
catchIt();
}
}
And call it as:
TryCatch(()=>{
int r = 44;
Console.WriteLine("Throwing Exception");
throw new Exception("something is wrong here");
}, ()=>{
Console.WriteLine("It was a mistake, will not try again");
});
In 2022 we have Func & Action doing the same, please see the demo code below which shows how this can be be used:
void Main()
{
var demo = new CallbackDemo();
demo.DoWork(()=> { Console.WriteLine("I have finished the work"); });
demo.DoWork((r)=> { Console.WriteLine($"I have finished the work here is the result {r}"); });
demo.DoWork(()=> { Console.WriteLine($"This is passed with func"); return 5;});
demo.DoWork((f)=> { Console.WriteLine($"This is passed with func and result is {f}"); return 10;});
}
// Define other methods and classes here
public class CallbackDemo
{
public void DoWork(Action actionNoParameter)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
actionNoParameter(); //execute
Console.WriteLine($"[The Actual Result is {result}]");
}
public void DoWork(Action<int> actionWithParameter)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
actionWithParameter(result); //execute
Console.WriteLine($"[The Actual Result is {result}]");
}
public void DoWork(Func<int> funcWithReturn)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
int c = funcWithReturn(); //execute
result += c;
Console.WriteLine($"[The Actual Result is {result}]");
}
public void DoWork(Func<int,int> funcWithParameter)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
result += funcWithParameter(result); //execute
Console.WriteLine($"[The Actual Result is {result}]");
}
}

What benefits can I take from SmtpClient.SendAsync's userToken object?

I was using SMTPClient.Send(mail) method to send emails, but then I saw, if the email id is not present (does not exists), my application waits till it receives the exception and then allows user to perform further tasks.
So I thought of using SMTPClient.SendAsync Method.
My doubt!! Where can this userToken Object be used which is passed as the parameter to the method? I searched many things online but din't find a good example. Even in MSDN they use it like this
string userState = "test message1";
client.SendAsync(message, userState);
But then what can it be really used for?
Thank you in advance.
You may use it in the following case: imagine that you have application for batch email sending. You compose message(different messages\attachments for every recipient, so you can't combine it into a single message), select for example 20 recipients and press button "Send All". For sending you use SendAsync and several SmtpClient instances from the "pool"(as SmtpClient doesn't allow to call SendAsync twice on one instance before previous call isn't completed).
You have a single SmtpClientSendCompleted handler for all SendAsync calls in which you should perform advanced logging: log result of the sending, names (addresses or even attachments) of the recipients of failed messages, but AsyncCompletedEventArgs can provide this information only with help of UserState. So the basic pattern for this purpose is to use custom user state object. So see the simplified example:
Interface which contains fields you will need in the handler:
public interface IEmailMessageInfo{
string RecipientName {get;set;}
}
Async state class:
/// <summary>
/// User defined async state for SendEmailAsync method
/// </summary>
public class SendAsyncState {
/// <summary>
/// Contains all info that you need while handling message result
/// </summary>
public IEmailMessageInfo EmailMessageInfo { get; private set; }
public SendAsyncState(IEmailMessageInfo emailMessageInfo) {
EmailMessageInfo = emailMessageInfo;
}
}
Here the code for sending email:
SmtpClient smtpClient = GetSmtpClient(smtpServerAddress);
smtpClient.SendCompleted += SmtpClientSendCompleted;
smtpClient.SendAsync(
GetMailMessage()
new SendAsyncState(new EmailMessageInfo{RecipientName = "Blah-blah"})
);
And the handler code example:
private void SmtpClientSendCompleted(object sender, AsyncCompletedEventArgs e){
var smtpClient = (SmtpClient) sender;
var userAsyncState = (SendAsyncState) e.UserState;
smtpClient.SendCompleted -= SmtpClientSendCompleted;
if(e.Error != null) {
tracer.ErrorEx(
e.Error,
string.Format("Message sending for \"{0}\" failed.",userAsyncState.EmailMessageInfo.RecipientName)
);
}
// Cleaning up resources
.....
}
Please, let me know if you need more details.
If you are using async(), you need to also have the event handler.
static void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
//to be implemented
}
This is fine to send email using Async() method. Hope this is helpful.

Categories

Resources