I've made a nice controller helper in my MVC project that allows me to return similarly structured result payloads when I make any ajax request. It looks like this:
public static JsonNetResult Responsify<T>(Func<T> func)
{
AjaxResponse response;
var result = new JsonNetResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
try
{
var resp = func();
response = AjaxResponse.SuccessResponse(resp);
result.Data = response;
}
catch (Exception e)
{
response = AjaxResponse.ErrorResponse(e.Message);
result.Data = response;
}
return result;
}
and it gets used like this:
[HttpPost]
public JsonNetResult Link(LinkInstruction instruction)
{
return ControllerHelper.Responsify(() => _linkHandler.Link(instruction));
}
where _linkHandler.Link is not async.
My issue is I have some async handlers that I would like to invoke using this pattern and I can't figure out how to await the function. I've tried Task.Factory.StartNew as well as wrapping the lambda in async/await and I can't quite figure it out.
Create a version of Responsify - call it ResponsifyAsync - that specifies that the passed Func must return a Task<T> (therefore meaning you can await it):
public async static JsonNetResult ResponsifyAsync<T>(Func<Task<T>> func)
{
...
}
This would do much what your current function does, but would await func() instead of just executing it.
You must then pass a Func delegate that returns a Task to this method. When calling an async library in the delegate, since the async method you're calling already returns a Task, this will simply take the form:
var x = await ResponsifyAsync(() => _someClass.MyMethodAsync(someParameter));
Or, if the method is parameterless...
var x = await ResponsifyAsync(_someclass.MyMethodAsync);
I'll let you flesh out the method but this format should set you off on the right lines.
Related
I was looking to make a settings method that executes the given settings and than execute the passed function/task. So instead of me typing a settings method each time
pubic void SomeMethod()
{
SettingsMethod()
//code
MainMethod()
}
public void SomeMethod2()
{
SettingsMethod()
/code...
MainMethod()
}
i wanted to do something like this:
public async static Task<T> ConfigureSettings<T>(Task<T> apiCallMethod, string localHost,int port, string riotToken)
{
client.BaseAddress = new Uri("https://"+localHost + ":"+port);
Console.WriteLine(client.BaseAddress);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", riotToken);
return await apiCallMethod;
}
public async static Task<string> GetSummonerId()
{
HttpResponseMessage response = await client.GetAsync("/lol-login/v1/session");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
And be able to call it like this:
string summonerId = LeagueApiProcessor.ConfigureSettings(LeagueApiProcessor.GetSummonerId(), LeagueAccount.LocalHost, LeagueAccount.LocalHostPort, LeagueAccount.RiotToken).Result;
As i tried to debug the code, the
HttpResponseMessage response = await client.GetAsync("/lol-login/v1/session");
is executing much earlier than the
client.BaseAddress = new Uri("https://"+localHost + ":"+port);
Console.WriteLine(client.BaseAddress);
and that is making it crash each time.
I've tried changing the return types, but that just breaks my code even more. What would be the best approach to solve this, again this was my attempt to make a SettingsMethod() execute every time before the "MainMethod()" without having to type it in each function.
Rather than accept an already running Task<T>, accept a Func that will start the Task<T> and return it when you call it. This can be as simple as:
public async static Task<T> ConfigureSettings<T>(Func<Task<T>> apiCallMethod, string localHost,int port, string riotToken)
{
//...
return await apiCallMethod();
}
And:
LeagueApiProcessor.ConfigureSettings(LeagueApiProcessor.GetSummonerId, ...
Notice that we're not invoking GetSummonerId when creating the arguments to ConfigureSettings, we're instead implicitly converting it to a delegate that is called at the end of ConfigureSettings instead.
For more complex examples (say, when you need to pass arguments to the second method), you may need to pass a lambda instead:
LeagueApiProcessor.ConfigureSettings(() => CallSomeTaskReturningMethod(arg1,arg2), ...
I have the following method and I want to call this from another method in the same controller. I have tried Task.Run but I can't seem to get it to work. This is .Net Core.
Here is the method I want to call:
[HttpGet("GetInfo")]
public async Task<ActionResult<InfoModel>> GetInfo()
{
InfoModel si = new InfoModel()
//Do work
return si
}
I then want to call it in another API method (below)
[HttpPost("GetOtherInfo")]
public async Task<ActionResult<NewModel>> GetOtherInfo(RequestInfoModel req)
{
NewModel ret = new NewModel();
//Do some work
//Need some data from the GetInfo() method
InfoModel im1 = GetInfo(); //This does not work
//Do work with im1
return ret;
}
I am looking for help on how to call GetInfo() from within GetOtherInfo(). Thank you in advance.
As a controller action GetInfo() is meant to be called by the framework when satisfying requests.
That fact that it has to be called in addition to that is a violation of SRP/SoC and a code smell. This indicates that a refactor is needed.
Either move that functionality into another method or another class if needed external to just this controller. That way it can be reused as needed.
private Task<InfoModel> getInfoAsync() {
InfoModel model = new InfoModel();
//Do async work
return model;
}
[HttpGet("GetInfo")]
public async Task<ActionResult<InfoModel>> GetInfo() {
InfoModel model = await getInfoAsync();
//and additional work needed specific to this request
return model;
}
[HttpPost("GetOtherInfo")]
public async Task<ActionResult<NewModel>> GetOtherInfo(RequestInfoModel req) {
NewModel ret = new NewModel();
//Do some work
//Need some info data
InfoModel im1 = await getInfoAsync();
//Do work with im1
return ret;
}
If your method is returning a Task<X>, you need to await the results.
[HttpPost("GetOtherInfo")]
public async Task<ActionResult<NewModel>> GetOtherInfo(RequestInfoModel req)
{
NewModel ret = new NewModel();
//Do some work
//Need some data from the GetInfo() method
InfoModel im1 = await GetInfo();
//Do work with im1
return ret;
}
Note that it's usually a good idea to follow conventions and add the Async suffix to methods returning a Task.
ex: GetInfoAsync()
I am working on an ASP.NET Core 2.2 application with Dapper. Virtually all methods follows the async/await pattern returning some sort of Task as a result.
Due to an issue we had with Dapper (I haven't personally investigated it, this part of the code I use as it is) but basically it boils down to the fact that if you want to execute in Transaction multiple async methods, which internally are calling other async methods and you may have several levels of nesting this way, you need to wrap all those method invocations within a single method which would be executed in a transaction.
The method that handles this is as follows:
public async Task<TOut> ExecuteInTransactionAsync<TOut>(
Delegate function,
params object[] parameters)
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var result = await ((Task<TOut>)function.DynamicInvoke(parameters))
.ConfigureAwait(false);
scope.Complete();
return result;
}
}
So I have a very complex Entity which is saved by calling Save to a lot of smaller entities. This part of the code is working OK and looks like this:
public async SaveEntireEntity(EntityDTO entityDTO)
{
return await _transactionProvider.ExecuteInTransactionAsync<dynamic>(
new Func<object, Task<dynamic>>(async dto => await SaveInTransaction(dto)),
new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age });
}
And the SaveInTransaction method looks like:
private async Task<dynamic> SaveInTransaction(dynamic dto)
{
var entityId = await nameService.Add(dto.Name);
await addressService.Add(dto.Address);
await ageService.Add(dto.Age);
return entityId;
}
so this is simplified, but indeed I am calling a multiple services here, which on their part are calling multiple repositories and this works fine.
The Problem I have is when it comes to updating the same entity within a transaction. The whole purpose of showing the Save logic was to point out that at the end because I have this return entityId; I am able to chain everything together without any problems. However, as it is right now, by default our Update methods are not returning anything and this is where I can't figure out how to implement the Update logic.
Currently I have this:
public async Task UpdateEntireEntity(UpdateEntityDTO, entityDTO)
{
await _transactionProvider.ExecuteInTransactionAsync<dynamic>(
new Func<object, Task<dynamic>>(async dto => await UpdateInTransaction(dto)),
new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age });
}
And UpdateInTransaction looks like this:
private async Task<dynamic> UpdateInTransaction(dynamic dto)
{
await UpdateName(dto.Name);
await UpdateAddress(dto.Address);
await UpdateAge(dto.Age);
return await Task.FromResult<dynamic>(null);
}
This seems to work at least based on the several tests I made, however I really don't like this part:
return await Task.FromResult<dynamic>(null);
To me it seems like an ugly hack. The Update methods were thought not to return any value and this is just too artificial.
And even the worst part is that I can not figure out how implement the update method without having to return something.
One thing I've tried is to change the declaration of UpdateInTransaction to
private async Task UpdateInTransaction(dynamic dto)
and when I call the method I change it to:
await _transactionProvider.ExecuteInTransactionAsync<dynamic>(
new Func<object, Task>( async dto => await UpdateInTransaction(dto)..
But I got the following exception:
AsyncStateMachineBox1[System.Threading.Tasks.VoidTaskResult,
<fully-qualified- name>.<<UpdateEntireEntity>b__0>d] to type
'System.Threading.Tasks.Task1[System.Threading.Tasks.Task]'
.
So basically that's it. Sorry for the long post. I would really appreciate some well explained answer.
I would avoid the use of Delegate since it isn't typed:
public async Task<TOut> ExecuteInTransactionAsync<TOut>(Func<Task<TOut>> function)
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var result = await (function()).ConfigureAwait(false);
scope.Complete();
return result;
}
}
This signature would mean you'd need to capture parameters rather than pass them:
public async Task<dynamic> SaveEntireEntity(EntityDTO entityDTO)
{
return await _transactionProvider.ExecuteInTransactionAsync(
async () => await SaveInTransaction(
new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age }));
}
Once you're using the strongly-typed Func<Task<T>> instead of Delegate in your method signature, you can create an overload for ExecuteInTransactionAsync as such:
public async Task ExecuteInTransactionAsync(Func<Task> function)
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await (function()).ConfigureAwait(false);
scope.Complete();
}
}
which can be used as such:
public async Task UpdateEntireEntity(UpdateEntityDTO entityDTO)
{
await _transactionProvider.ExecuteInTransactionAsync(
async () => await UpdateInTransaction(
new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age }));
}
private async Task UpdateInTransaction(dynamic dto)
{
await UpdateName(dto.Name);
await UpdateAddress(dto.Address);
await UpdateAge(dto.Age);
}
You can change
private async Task<dynamic> UpdateInTransaction(dynamic dto)
{
await UpdateName(dto.Name);
await UpdateAddress(dto.Address);
await UpdateAge(dto.Age);
return await Task.FromResult<dynamic>(null);
}
to
private async Task<dynamic> UpdateInTransaction(dynamic dto)
{
await UpdateName(dto.Name);
await UpdateAddress(dto.Address);
await UpdateAge(dto.Age);
return null;
}
I have:
public static async Task<string> httpRequest(HttpWebRequest request)
I would like to do this:
string rez1;
static void test()
{
Task.Factory.StartNew(() => {
//How can I get result like rez1= httpRequest((HttpWebRequest)HttpWebRequest.Create("google.com")));
//or catch WebException here.
});
}
How can I do it?
Thanks
You have it mixed up a little bit. When your methods signature is:
public static async Task<string> httpRequest(HttpWebRequest request)
That means "this method is invoked asynchronously, i will call it and it will return imminently with a promise to finish in the future".
You should change your method to look like this:
Edit
Fixed the code according to the comments made.
public static Task<string> httpRequest(HttpWebRequest request)
{
return Task.Factory.Startnew(() = > {
return HttpWebRequest.Create("google.com")
}
}
When your method is marked as async, that means the caller might think it is "pure" async, which means no threads execute behind the scenes. If you do fire up a new thread (as you are doing here, by using a Thread Pool thread) you should explicitly comment your method and tell your invoker that he will be firing up a new thread.
You can save yourself firing up a new Task if you're using .NET 4.5 and have access to the new HttpClient class.
You can change your method to look like this:
public static async Task<string> httpRequest(HttpWebRequest request)
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync("http://www.google.com")
var stringResult = await response.Content.ReadAsStringAsync();
return stringResult;
}
All calls to the service should work through personal channel. So all methods which have access to the server proxy should look like this one:
public async Task<SDRLocation[]> FindLocationsAsync(string searchString)
{
ChannelFactory<IQueryService> channel = new ChannelFactory<IQueryService>("SomeServ_IQuery");
channel.Open();
SomeProxy = channel.CreateChannel();
Location[] locationEntitiesFound = await SomeProxy.FindLocationsAsync(searchString);
((IChannel)SomeProxy ).Close();
return locationEntitiesFound.Select(x => new SDRLocation(x)).ToArray();
}
But because I have a lot of methods like this service calls I tried to avoid code duplication and create this method wrapper:
public TResult HandleServiceCall<TResult>(Func<IPlantOrgQueryService, TResult> serviceMethod)
{
ChannelFactory<IQueryService> channel = new ChannelFactory<IQueryService>("SomeServ_IQuery");
channel.Open();
IQueryService newProxy = channel.CreateChannel();
TResult results = serviceMethod(newProxy);
((IChannel)newProxy).Close();
return results;
}
now I expect to make everywhere calls like this :
public async Task<SDRLocation[]> FindLocationsAsync(string searchString)
{
Location[] locationEntitiesFound = await HandleServiceCall(x => x.FindLocationsAsync(searchString));
return locationEntitiesFound.Select(x => new SDRLocation(x)).ToArray();
}
But I end up with error "The communication object, System.ServiceModel.Channels.ClientReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted."
Not understand what is wrong because method without HandleServiceCall works just fine...
Help please
The type of TResult will let you know what's wrong. It's Task<Location[]>. So you're disposing the proxy (via Close) before the asynchronous call is complete.
The fix is to await the Task before calling Close, just like your original code is doing. This should do the trick:
public async Task<TResult> HandleServiceCall<TResult>(Func<IPlantOrgQueryService, Task<TResult>> serviceMethod)
{
ChannelFactory<IQueryService> channel = new ChannelFactory<IQueryService>("SomeServ_IQuery");
channel.Open();
IQueryService newProxy = channel.CreateChannel();
TResult results = await serviceMethod(newProxy);
((IChannel)newProxy).Close();
return results;
}