This question already has answers here:
Anonymous interface implementation
(2 answers)
Closed 7 years ago.
I'm making a videogame and I need clients and server to communicate (in Unity).
The message from the server needs to be converted to a specific type based on the request: for example, the registration process will return just a bool in case of success or not; the login process must return a lot of information in a dictionary for the user who logged in (level, experience, game progress, etc.)
In java I used to do something like this:
net().post(url.toString(), safeUrl, new Callback<String>()
{
public void onFailure(Throwable t)
{
// do something to handle the failure...
}
public void onSuccess(String json)
{
// do something...
}
});
Basically the net().post was sending a post http request to the server, and once it was done the server was calling the correct method.
I read that C# can't do this, but that delegates can achieve something similar, do you know how? so basically I just send to the post request a method as parameter and handle it? Also is it possible to use generic types to handle the different types of requests?
Or Is there a better way to do this?
You have two alternatives here.
You can use Action<string> to pass in anonymous callbacks to your method.
Or...
you dive into the async/await world, which is the recommended approach.
Using anonymous delegates
Your Post signature would look like this:
public void Post(string url, string safeUrl, Action<string> onSuccess, Action<Exception> onFailure);
Notice that you must have an argument for each callback result.
Inside your Post method, you should call the callbacks when done.
From the client (or caller), you would call it like this:
Post(url, safeUrl, response =>
{
// Success callback body
}, exception =>
{
// Failure callback body
});
Using async/await (I don't know if this approach is supported in Unity)
Your Post signature would look like this:
public async Task<string> Post(string url, string safeUrl);
Inside your Post method, when sending the request you should use a method that support async/await, such as HttpClient.PostAsync():
// This is just an example
public async Task<string> Post(string url, string safeUrl)
{
HttpResponseMessage response = await httpClient.PostAsync(url, null);
string content = await response.Content.ReadAsStringAsync();
return content;
}
You would call it like this:
Task<string> fetchingResponse = Post(url, safeUrl);
// You can keep doing things while you wait for the response ...
try
{
string response = await fetchingResponse;
// Success logic here
}
catch(Exception ex)
{
// Failure logic here
}
Notice that there are no callbacks.
Also, keep in mind that your caller method (i.e. the method that is executing Post) should also be anotated with async and should also return a Task or Task<T> instance.
No, there is no direct equivalent to that in c#, however you may use delegates, e.g. as follows:
class Callback {
private readonly Action<Exception> onFailure;
private readonly Action<string> onSuccess;
public CallBack(Action<Exception> onFailure, Action<string> onSuccess) {
this.onFailure = onFailure;
this.onSuccess = onSuccess;
}
public void FireFailure(Exception e) {this.OnFailure(e);}
public void FireSuccess(string json) {this.OnSuccess(json);}
}
Now it is up to your net().post-method to fire the approriate events.
Related
My goal: Pass data do specific client who is connected to server and get results without calling Server method.
I tried use SignalR to do this (because It's very easy tool for me), but I can't get results (now I know why). I am working on ASP.NET Core 3.1.
My question: Is there SignalR alternative with "return value to server" functionality (call method with params on target client and get results)?
SignalR is usually used in a setup where there are multiple clients and a single server the clients connect to. This makes it a normal thing for clients to call the server and expect results back. Since the server usually does not really care about what individual clients are connected, and since the server usually broadcasts to a set of clients (e.g. using a group), the communication direction is mostly used for notifications or broadcasts. Single-target messages are possible but there isn’t a built-in mechanism for a request/response pattern.
In order to make this work with SignalR you will need to have a way for the client to call back the server. So you will need a hub action to send the response to.
That alone doesn’t make it difficult but what might do is that you will need to link a client-call with an incoming result message received by a hub. For that, you will have to build something.
Here’s an example implementation to get you starting. The MyRequestClient is a singleton service that basically encapsulates the messaging and offers you an asynchronous method that will call the client and only complete once the client responded by calling the callback method on the hub:
public class MyRequestClient
{
private readonly IHubContext<MyHub> _hubContext;
private ConcurrentDictionary<Guid, object> _pendingTasks = new ConcurrentDictionary<Guid, object>();
public MyRequestClient(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
public async Task<int> Square(string connectionId, int number)
{
var requestId = Guid.NewGuid();
var source = new TaskCompletionSource<int>();
_pendingTasks[requestId] = source;
await _hubContext.Clients.Client(connectionId).SendAsync("Square", nameof(MyHub.SquareCallback), requestId, number);
return await source.Task;
}
public void SquareCallback(Guid requestId, int result)
{
if (_pendingTasks.TryRemove(requestId, out var obj) && obj is TaskCompletionSource<int> source)
source.SetResult(result);
}
}
In the hub, you then need the callback action to call the request client to complete the task:
public class MyHub : Hub
{
private readonly ILogger<MyHub> _logger;
private readonly MyRequestClient _requestClient;
public MyHub(ILogger<MyHub> logger, MyRequestClient requestClient)
{
_logger = logger;
_requestClient = requestClient;
}
public Task SquareCallback(Guid requestId, int number)
{
_requestClient.SquareCallback(requestId, number);
return Task.CompletedTask;
}
// just for demo purposes
public Task Start()
{
var connectionId = Context.ConnectionId;
_ = Task.Run(async () =>
{
var number = 42;
_logger.LogInformation("Starting Square: {Number}", number);
var result = await _requestClient.Square(connectionId, number);
_logger.LogInformation("Square returned: {Result}", result);
});
return Task.CompletedTask;
}
}
The Start hub action is only for demo purposes to have a way to start this with a valid connection id.
On the client, you then need to implement the client method and have it call the specified callback method once it’s done:
connection.on('Square', (callbackMethod, requestId, number) => {
const result = number * number;
connection.invoke(callbackMethod, requestId, result);
});
Finally, you can try this out by invoking the Start action by a client:
connection.invoke('Start');
Of course, this implementation is very basic and will need a few things like proper error handling and support for timing out the tasks if the client didn’t respond properly. It would also be possible to expand this to support arbitrary calls, without having you to create all these methods manually (e.g. by having a single callback method on the hub that is able to complete any task).
I am having web api, which is non async.
From that web api, i am calling method
SmtpClient.SendMailAsync(email);
This email get sent to respective person, but the next web api request get fails.
public class TestController : ApiController
{
public TestController(TestService testService)
{
_testService = testService;
}
public IHttpActionResult Post(data)
{
_testService.SendEmail(data);
}
}
public class TestService
{
public async Task SendEmail(MailMessage email)
{
SmtpClient client = new SmtpClient();
client.SendMailAsync(email)
}
}
From the days before the async/await pattern was introduced, there are still many non-asychronous functions around in Framework classes.
SmtpClient client is one of the classes old enough for this. The SendFunction are the droids you are look for:
https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient.send
While the naming is a bit off and they return void, those seems to be the pre-async functions. Failure should be communicated via Exceptions in both cases.
Given your comment that you want to have SendEmail behave like fire & forget, I would propose using
Task.Run(() => _testService.SendEmail(data));
This will give the unit of work to the threadpool and free your request from the duty of waiting for this task. Generelly this is advised for fire & forget.
As a rule of thumb otherwise, it's generally a bad idea to call asynchronous things from a synchronous context. Do async all the way, or be prepared for deadlocking. For example, you could simply make your controller actions asynchronous as well.
If you will need use some result of sending:
public async IHttpActionResult Post(data)
{
var t = Task.Run(() =>_testService.SendEmail(data));
var result = await t;
// do something with result
}
- but of course your SendEmail function should return Task<> instead of Task ...
I'm looking to a design pattern to help simplify my code.
My code is using HttpClient to call a web API that gets or posts information, but each session requires a login call first where a cookie is returned as ID, and a logout call is made at the end to close the connection. So my web API class looks like this:
public class APIHelper
{
public HttpClient myClient { get; set; }
public async void Login()
{
using (HttpResponseMessage response = await myClient.PostAsync("loginAddress", "loginInput"))
{
if (response.IsSuccessStatusCode)
{
//save cookie
}
}
}
public async void Logout()
{
using (HttpResponseMessage response = await myClient.PostAsync("logoutAddress", ""))
{
if (response.IsSuccessStatusCode)
{
//session ends
}
}
}
public void GetOrder() {...}
public void NewOrder(object OrderData) {...}
public void GetCustomer() {...}
public void NewCustomer(object CustomerData) {...}
}
And to use them, I would simply call them in order:
public Main()
{
APIHelper.Login();
APIHelper.GetOrder(); //or NewOrder, or GetCustomer, or any other actual API calls
APIHelper.Logout();
}
Is there anyway I can place the Login/Logout calls inside each of the actual API calls so I don't have to type them up for each call? Ideally I just have to set up the structure once, then for whatever API calls I create, the system will automatically call the Login/Logout at beginning/end. Which design pattern addresses this kind of issue? Any simple example would be very helpful!
Thank you. SC
=====================================
Edit: Trying out Dymanoid's suggestion
Thanks for the quick reply! I think it should work, but I got another wrinkle: "An object reference is required for the non-static field, method, or property 'APIHelper.GetOrder()'
Looking at the new code, it seems I need the myClient object (which is a HttpClient object) through out the session:
public void DoMyJob(Action job)
{
var myClient = new APIHelper(); //initiates a HttpClient object
myClient.Login();
try
{
job();
}
finally
{
myClient.Logout();
}
}
But when calling the job function from outside:
DoMyJob(APIHelper.GetOrder);
I don't have the object yet. Is this easy to work around? Sorry I didn't make that clear in the OP, my actual original call looks like this:
var myClient = new APIHelper();
myClient.Login();
myClient.GetOrder();
myClient.Logout();
But thanks to your suggestion, I think it's better to put the initilization inside the DoMyJob method.
Use template method + strategy for that.
void DoMyJob(Action job)
{
APIHelper.Login();
try
{
job();
}
finally
{
APIHelper.Logout();
}
}
Usage looks like this:
DoMyJob(() => APIHelper.GetOrder());
or
DoMyJob(APIHelper.GetOrder);
or
DoMyJob(() => { APIHelper.GetOrder(); APIHelper.NewOrder(); } );
I have an API which is responsible for inserting text message details in database.
It does by making synchronous call to repository which I think could be implemented asynchronous way.How can I achieve this? Or what could be the best way to handle this scenario.Code snippet example is highly appreciated as I am still getting my ahead wrapping around .NET.
api:
public IHttpActionResult SendSMSNotification([FromBody] SMSNotification smsNotification)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_service.SendSMS(smsNotification);
return Ok();
}
Service:
internal void SendSMS(SMSNotification smsNotification)
{
_repository.Notify(_mapperService.GetSMSNotification(smsNotification));
}
mapper:
public SMSNotification GetSMSNotification(SMSNotification message)
{
return AutoMapper.Mapper.Map<SMSNotification>(message);
}
repo:
public virtual bool Notify(SMSNotification request)
{
using (var sql = _sqlMapper.CreateCommand('Database', 'Stored proc'))
{
sql.AddParam("#fMessage", request.Message);
//..............
//.............. more params
var retvalParamOutput = sql.AddOutputParam("#fRetVal", System.Data.SqlDbType.Int);
sql.Execute();
return retvalParamOutput.GetSafeValue<int>() == 1;
}
}
sql here is a custom thing and it has following methods:
public static int Execute(this IDataCommand #this);
[AsyncStateMachine(typeof(<ExecuteAsync>d__1))]
public static Task<int> ExecuteAsync(this IDataCommand #this);
Changing a blocking, typically IO-bound call (such as database, network or file system work) to async can make your app scale better.
This does have a flow-on affect through your API. That is, you need to be awaiting on asynchronous calls all the way up to the top-most call, otherwise, somewhere is going to block and you're just lost the benefit of calling an async API.
To demonstrate that, let's start from the bottom at the repository call, as that's the possibly expensive blocking operation can be made async. We alter sql.Execute to use the asynchronous version ExecutAsync version instead:
repo:
public virtual async Task<bool> Notify(SMSNotification request)
{
using (var sql = _sqlMapper.CreateCommand('Database', 'Stored proc'))
{
sql.AddParam("#fMessage", request.Message);
//..............
//.............. more params
var retvalParamOutput = sql.AddOutputParam("#fRetVal", System.Data.SqlDbType.Int);
await sql.ExecuteAsync();
return retvalParamOutput.GetSafeValue<int>() == 1;
}
}
Now here we have to change the signature of the method to return a Task wrapping a result of bool.
We also mark the method as async, so then we can use the "await" operator further down. Without doing this, we'd have to do more refactoring to manipulate and return the Task result ourselves, but the "async" modifier and "await" keyword let the compiler do that magic for us, and the rest of our code mostly looks like normal.
The mapper call doesn't really need to change:
mapper:
public SMSNotification GetSMSNotification(SMSNotification message)
{
return AutoMapper.Mapper.Map<SMSNotification>(message);
}
The service call is now making a call to an async method, so because we want to await and not block on that async call, we have to also change this previously void method to an async method. Note we change it from "void" to "async Task"; you CAN mark a void method as "async void", but that's intended as a workaround for event handlers in Windows Forms and WPF apps; in every other case you want to change a "void" method to "async Task" when making it async.
Service:
internal async Task SendSMS(SMSNotification smsNotification)
{
await _repository.Notify(_mapperService.GetSMSNotification(smsNotification));
}
Then finally, our API call can be made async, and await our service call:
api:
public async Task<IHttpActionResult> SendSMSNotification([FromBody] SMSNotification smsNotification)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
await _service.SendSMS(smsNotification);
return Ok();
}
It's sometimes recommended that after you do a refactor like this, to rename the methods to end in "Async" by convention; however I don't think this is really compulsory, as so much of the .NET API surface is becoming async, it's almost redundant now.
It's worth getting your head around the async / await stuff though; I've tried to keep the example relatively brief. But I hope this at least gets you started.
I need to create a method that will connect to my Azure SQL database and read some data. All the methods that call this method are async methods but I don't think I can make the ReadStateProvinceListAsync an async one.
What's the right approach in these situations? Just ignore the "warning" from the compiler that I have an async method that does not use the await keyword or make the ReadStateProvinceListAsync method a regular synchronous method in which case the GetStateProvinceListAsync method will give me the same warning.
I want to do it right -- by the book. That's why I want to learn the right approach.
public static async Task<List<StateProvince>> GetStateProvinceListAsync(string countryId)
{
// Check to see if I already have this data cached
if(gotData)
{
// Life is good! Get data from cache.
}
else
{
// Don't have the data cached. Call the DB read method
statesList = await ReadStateProvinceListAsync(countryId)
}
}
private static async Task<List<StateProvince>> ReadStateProvinceListAsync(string countryId)
{
// Call Azure SQL Database to read data. No async code here!
}
There's absolutely no value in marking a synchronous method with the async keyword. If your method doesn't make any asynchronous calls make it synchronous:
public static List<StateProvince> GetStateProvinceListAsync(string countryId)
{
// Check to see if I already have this data cached
if(gotData)
{
// Life is good! Get data from cache.
}
else
{
// Don't have the data cached. Call the DB read method
statesList = ReadStateProvinceList(countryId)
}
}
private static List<StateProvince> ReadStateProvinceList(string countryId)
{
// Call Azure SQL Database to read data. No async code here!
}
If for some reason you must return a task (e.g. it's an interface or abstract implementation) use Task.FromResult to return a Task synchronously:
private static Task<List<StateProvince>> ReadStateProvinceListAsync(string countryId)
{
return Task.FromResult(ReadStateProvinceList());
}