I have problem when I trying to return task result from task async function my function:
public async Task<IEnumerable<string>> Run()
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = "API Key",
ApplicationName = this.GetType().ToString()
});
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = "anwar jibawi"; // Replace with your search term.
searchListRequest.MaxResults = 50;
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = await searchListRequest.ExecuteAsync();
List<string> videos = new List<string>();
List<string> channels = new List<string>();
List<string> playlists = new List<string>();
// Add each result to the appropriate list, and then display the lists of
// matching videos, channels, and playlists.
foreach (var searchResult in searchListResponse.Items)
{
switch (searchResult.Id.Kind)
{
case "youtube#video":
string thumbnail = searchResult.Snippet.Thumbnails.Default__.Url;
videos.Add(String.Format("{0} ({1}) {2}", searchResult.Snippet.Title, searchResult.Id.VideoId, thumbnail));
break;
case "youtube#channel":
channels.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.ChannelId));
break;
case "youtube#playlist":
playlists.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.PlaylistId));
break;
}
}
return videos;
//Console.WriteLine(String.Format("Videos:\n{0}\n", string.Join("\n", videos)));
//Console.WriteLine(String.Format("Channels:\n{0}\n", string.Join("\n", channels)));
//Console.WriteLine(String.Format("Playlists:\n{0}\n", string.Join("\n", playlists)));
}
here I'm calling async function:
public ActionResult Index()
{
Task<IEnumerable<string>> task = new Search().Run();
task.Wait();//if remove this line it will work fine but without any result
var x = task.Result;//if remove this line it will work fine but without any result
return View();
}
Why its hanging when I call task.Wait() or task.Reslut
Assuming this is an ASP.NET application, you shouldn't use .Result (or .Wait()) as it will result in deadlock (as you've found out)
Instead, change your Index method to this
public async Task<ActionResult> Index()
{
var x = await new Search().Run();
return View();
}
Related
I have a controller which is following;
public class ScheduleController: ControllerBase
{
private readonly IScheduleService _scheduleService;
public ScheduleController(IScheduleService scheduleService)
{
_scheduleService = scheduleService;
}
[HttpGet]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleResponseDto>> GetDraftSchedule(int plantCode)
{
var schedule = await _scheduleService.GetLatestScheduleForPlant(plantCode);
return Ok(schedule);
}
[HttpPost]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleResponseDto>> PostSchedule(int plantCode, List<ScheduleInputItemDto> scheduleInputItems)
{
var schedule = await _scheduleService.AddNewSchedule(plantCode, scheduleInputItems);
return Ok(schedule);
}
[HttpPost("items")]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleItemResponseDto>> PostScheduleItem(int scheduleId, ScheduleInputItemDto scheduleInputItem)
{
var scheduleItem = await _scheduleService.AddItemToSchedule(scheduleId, scheduleInputItem);
return Ok(scheduleItem);
}
[HttpPut("items/{itemId}")]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleItemResponseDto>> PutScheduleItem(int scheduleId, int itemId, ScheduleInputItemDto scheduleInputItem)
{
var scheduleItem = await _scheduleService.ChangeScheduleItem(scheduleId, itemId, scheduleInputItem);
return Ok(scheduleItem);
}
}
User adding new schedule and items to schedule. I need to check if items are overlapping by their start date and end date. I have been trying to find a solution to prevent items to be added if they are overlapping.
In AddItemToSchedule method I need to check if they are overlapping.
public async Task<ScheduleResponseDto> AddItemToSchedule(int scheduleId, ScheduleInputItemDto scheduleItem)
{
var scheduleWithId = await _scheduleRepository.GetScheduleById(scheduleId);
scheduleWithId.AddItem(
start: scheduleItem.Start,
end: scheduleItem.End,
cementType: scheduleItem.CementType,
now: DateTime.UtcNow);
await _scheduleRepository.Update(scheduleWithId);
return scheduleWithId.MapToScheduleDto();
}
And Also same thing for ChangeScheduleItem method.
public async Task<ScheduleResponseDto> ChangeScheduleItem(int scheduleId, int itemId, ScheduleInputItemDto scheduleInputItem)
{
var now = DateTime.UtcNow;
var schedule = await _scheduleRepository.GetScheduleById(scheduleId);
schedule.UpdateItem(itemId, scheduleInputItem.Start, scheduleInputItem.End, scheduleInputItem.CementType, now);
await _scheduleRepository.Update(schedule);
return schedule.MapToScheduleDto();
}
The first test is for adding new item to schedule and testing with Task.WhenAll() what if two different user call the endpoint with the same two items at the same time.
[Fact]
public async Task GivenScheduleWithNoItems_WhenTwoSimultaneousIdenticalAddItemRequests_ThenOneItemIsAddedAndTheOtherRejected()
{
//Setup
var fixture = new Fixture();
var plantCode = fixture.Create<int>().ToString();
var itemToAdd = new ScheduleInputItemDto
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddHours(1),
CementType = "CEM-I"
};
var addScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var latestScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var addItemForScheduleRequest = (string scheduleId) => NewRequest
.AddRoute("schedule/items")
.AddQueryParams("scheduleId", scheduleId);
// Exercise
await addScheduleRequest.Post(new ScheduleInputItemDto[]{});
// First let's get the schedule before adding any items. This schedule is currently empty..
var scheduleBeforeAddition = await latestScheduleRequest.Get<ScheduleResponseDto>();
var scheduleId = scheduleBeforeAddition.ScheduleId.ToString();
var addItemRequest = addItemForScheduleRequest(scheduleId);
// Simultaneously start two tasks that will make the same exact item addition request.
// This is a race condition, the first request should pass and the second should fail.
var itemAddResponses = await Task.WhenAll(addItemRequest.Post(itemToAdd, false), addItemRequest.Post(itemToAdd, false));
//Finally let's get the schedule after the item addition requests. It should have only one item in it.
var scheduleAfterAddition = await latestScheduleRequest.Get<ScheduleResponseDto>();
// Verify
scheduleBeforeAddition.ScheduleItems.Count.Should().Be(0);
//TEST FAILS HERE - only one of the items should be added and the second should cause a conflict
scheduleAfterAddition.ScheduleItems.Count.Should().Be(1);
var failures = itemAddResponses.ToList().Where(it => it.IsSuccessStatusCode == false);
var successes = itemAddResponses.ToList().Where(it => it.IsSuccessStatusCode == true);
failures.Count().Should().Be(1);
successes.Count().Should().Be(1);
}
The second test is for also checking the same thing during changing the item in the schedule.
[Fact]
public async Task GivenScheduleWithItem_WhenTwoClientsAreChangingTheSingleItem_ThenItemModificationShouldHappenInSequence()
{
//Setup
var fixture = new Fixture();
var plantCode = fixture.Create<int>().ToString();
var itemDto = new ScheduleInputItemDto
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddHours(1),
CementType = "CEM-I"
};
var addScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var latestScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var changeItemForScheduleRequest = (string scheduleId, string itemId) => NewRequest
.AddRoute($"schedule/items/{itemId}")
.AddQueryParams("scheduleId", scheduleId);
//Exercise
//Make new schedule
await addScheduleRequest.Post(new List<ScheduleInputItemDto> { itemDto });
var scheduleBeforeChanges = await latestScheduleRequest.Get<ScheduleResponseDto>();
var scheduleId = scheduleBeforeChanges.ScheduleId.ToString();
var existingItemId = scheduleBeforeChanges.ScheduleItems.First().ScheduleItemId;
var itemChangeRequest = changeItemForScheduleRequest(scheduleId, existingItemId.ToString());
// Send two simultaneous item change requests
var itemChangeResponses = await Task.WhenAll(itemChangeRequest.Put(itemDto, false), itemChangeRequest.Put(itemDto, false));
//Get the schedule after item change requests, should have only one item and the item should have an update counter of only 1
var scheduleAfterChanges = await latestScheduleRequest.Get<ScheduleResponseDto>();
//verify
scheduleBeforeChanges.ScheduleItems.Count.Should().Be(1);
scheduleBeforeChanges.ScheduleItems.First().NumberOfTimesUpdated.Should().Be(0);
scheduleAfterChanges.ScheduleItems.Count.Should().Be(1);
scheduleAfterChanges.ScheduleItems.First().NumberOfTimesUpdated.Should().Be(1);
var failures = itemChangeResponses.ToList().Where(it => it.IsSuccessStatusCode == false);
var successes = itemChangeResponses.ToList().Where(it => it.IsSuccessStatusCode == true);
//TEST FAILS HERE, as one of the calls should fail
failures.Count().Should().Be(1);
successes.Count().Should().Be(1);
}
In conclusion I need to prevent methods to add overlapping items in the schedule.
Example Json Input is the following;
{
"scheduleId": 12132891,
"plantId": 1213,
"updatedOn": "2021-12-01T12:44:17Z",
"scheduleItems": [
{
"scheduleItemId": 1,
"cementType": "CEM-I",
"start": "2021-11-23T00:00:00Z",
"end": "2021-11-23T02:15:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
},
{
"scheduleItemId": 2,
"cementType": "CEM-II",
"start": "2021-11-23T03:00:00Z",
"end": "2021-11-23T10:30:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
},
{
"scheduleItemId": 3,
"cementType": "CEM-III",
"start": "2021-11-23T10:30:00Z",
"end": "2021-11-23T11:00:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
}
]
}
How to run an async Task without blocking other tasks?
I have one function that iterates though a List but the problem is that when the function is called other functions won't work again until the first function is done. What are the ways of making the HandleAsync function non-blocking ?
public static async Task HandleAsync(Message message, TelegramBotClient bot)
{
await Search(message, bot); // This should be handled without working other possible functions. I have a function similar to this but which doesn't iterate though any list.
}
private static async Task Search(Message message, TelegramBotClient bot)
{
var textSplit = message.Text.Split(new[] {' '}, 2);
if (textSplit.Length == 1)
{
await bot.SendTextMessageAsync(message.From.Id, "Failed to fetch sales. Missing game name. ",
ParseMode.Html);
}
else
{
var search = await Program.itad.SearchGameAsync(textSplit[1], limit: 10, cts: Program.Cts);
if (search.Data != null)
{
var builder = new StringBuilder();
foreach (var deal in search.Data.List)
{
var title = deal.Title;
var plain = deal.Plain;
var shop = deal.Shop != null ? deal.Shop.Name : "N/A";
var urls = deal.Urls;
var priceNew = deal.PriceNew;
var priceOld = deal.PriceOld;
var priceCut = deal.PriceCut;
builder.AppendLine($"<b>Title:</b> {title}");
builder.AppendLine($"<b>Shop:</b> {shop}");
builder.AppendLine();
builder.AppendLine($"<b>Price:</b> <strike>{priceOld}€</strike> | {priceNew}€ (-{priceCut}%)");
var buttons = new[]
{
new[]
{
InlineKeyboardButton.WithUrl("Buy", urls.Buy.AbsoluteUri),
InlineKeyboardButton.WithUrl("History",
urls.Game.AbsoluteUri.Replace("info", "history"))
}
};
var keyboard = new InlineKeyboardMarkup(buttons);
var info = await Program.itad.GetInfoAsync(plain, cts: Program.Cts);
var image = info.Data.GameInfo.Image;
if (image == null) image = new Uri("https://i.imgur.com/J7zLBLg.png");
await TelegramBot.Bot.SendPhotoAsync(message.From.Id, new InputOnlineFile(image.AbsoluteUri),
builder.ToString(), ParseMode.Html, replyMarkup: keyboard,
cancellationToken: Program.Cts.Token);
builder.Clear();
}
}
else
{
await bot.SendTextMessageAsync(message.From.Id, "Failed to fetch sales. Game not found. ",
ParseMode.Html);
}
}
I am trying to call HttpClient request inside for loop as follows. It needs to do multiple consecutive calls to third party rest api.
But it only gives me fist service call result while loop exit before getting result from rest of the service call.
private void Search()
{
try
{
var i = 1;
using (var httpClient = new HttpClient())
{
while (i < 5)
{
string url = "https://jsonplaceholder.typicode.com/posts/" + i;
var response = httpClient.GetAsync(url).Result;
string jsonResult = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(jsonResult.ToString());
i++;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
When I run with debug points the program gives me all the result. But when I run it without debug points it gives me only the first result.
I tried this with using async, await methods too. It also gives me same result.
As I feel Program needs to wait until the async call returns data.
Please help me to solve this.
EDIT - async way
private async Task<string> SearchNew()
{
try
{
var i = 1;
var res = string.Empty;
using (var httpClient = new HttpClient())
{
while (i < 5)
{
string url = "https://jsonplaceholder.typicode.com/posts/" + i;
var response = httpClient.GetAsync(url).Result;
string jsonResult = await response.Content.ReadAsStringAsync();
res = res + jsonResult + " --- ";
i++;
}
}
return res;
}
catch (Exception ex)
{
return ex.Message;
}
}
Both are giving same result.
There's a few things here that you should be doing. First, move the HttpClient creation outside of your method and make it static. You only need one of them and having multiple can be really bad for stability (see here):
private static HttpClient _client = new HttpClient();
Next, extract the calls to the HttpClient into a single method, something simple like this:
//Please choose a better name than this
private async Task<string> GetData(string url)
{
var response = await _client.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
And finally, you create a list of tasks and wait for them all to complete asynchronously using Task.WhenAll:
private async Task<string[]> SearchAsync()
{
var i = 1;
var tasks = new List<Task<string>>();
//Create the tasks
while (i < 5)
{
string url = "https://jsonplaceholder.typicode.com/posts/" + i;
tasks.Add(GetData(url));
i++;
}
//Wait for the tasks to complete and return
return await Task.WhenAll(tasks);
}
And to call this method:
var results = await SearchAsync();
foreach (var result in results)
{
Console.WriteLine(result);
}
I'm using the Azure Management Libraries (specifically fluent) to create web request towards their api to get a list of my databases under my subscription. I'm able to get an instance of the sqlserver using fluent but am unable to get a list of all databases under a specific server.
Define and delete work fine it is just the list() function.
I've tried using it for sqlserver.firewallrules and the list function doesn't work there as well.
Here is some code:
The log at some point just pauses then writes "has exited with code 0"
public async Task<List<String>> getSqlDatabaseList()
{
System.Diagnostics.Debug.WriteLine("Starting to get database list");
List<string> dbNameList = new List<string>();
//the var azure is defined earlier in the project and is authenticated.
var sqlServer = await azure.SqlServers.GetByResourceGroupAsync("<resource group name>", "<server Name>");
//The code below successfully writes the server name
System.Diagnostics.Debug.WriteLine(sqlServer.Name);
//The code below here is where everyting stop and "has exited with code 0" happens after a few seconds of delay
var dbList = sqlServer.Databases.List();
//Never reaches this line
System.Diagnostics.Debug.WriteLine("This line never is written");
foreach (ISqlDatabase db in dbList)
{
dbNameList.Add(db.Name);
}
return dbNameList;
}
Clarification:
I'm using ASP.NET MVC
Here is how my controller method accesses the class method. Resource Manager is the name of the class that implements getSQlDatabaseList();
// GET: Home
public async Task<ActionResult> Index()
{
ResourceManager rm = new ResourceManager();
List<string> test = await rm.getSqlDatabaseList();
//Never Gets to this line of code and never calls the for each or anything after
foreach (var item in test)
{
System.Diagnostics.Debug.WriteLine(item);
}
System.Diagnostics.Debug.WriteLine("Is past for each");
//AzureManager azm = await AzureManager.createAzureManager();
//await azm.getResourceGroupList();
return View(new UserLogin());
}
According to your code and description, I guess the reason why your code couldn't create the table is about your async getSqlDatabaseList.
I guess you call this method in console main method or something else.
If your main method is executed completely, your async method getSqlDatabaseList isn't execute the completely and return the list of the string. It will end all async method.
I suggest you could add await or result key keyword when calling the getSqlDatabaseList method to wait the thread execute the method completely.
More details, you could refer to below test demo.
static void Main(string[] args)
{
//use result to wait the mehtod executed completely
List<String> test = getSqlDatabaseList().Result;
foreach (var item in test)
{
Console.WriteLine(item);
}
Console.Read();
}
public static async Task<List<String>> getSqlDatabaseList()
{
//System.Diagnostics.Debug.WriteLine("Starting to get database list");
List<string> dbNameList = new List<string>();
var credentials = SdkContext.AzureCredentialsFactory.FromFile(#"D:\Auth.txt");
var azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithDefaultSubscription();
var sqlServer = await azure.SqlServers.GetByResourceGroupAsync("groupname", "brandotest");
var dbList = sqlServer.Databases.List();
foreach (ISqlDatabase db in dbList)
{
dbNameList.Add(db.Name);
}
return dbNameList;
}
Update:
According to your description, I have created a test MVC application. As you say I have reproduce your issue.
I think there are something wrong with the azure management fluent SDK.
Here is a workaround, I suggest you could directly send rest api to get the database.
More details, you could refer to below codes:
Send the request to below url:
https://management.azure.com/subscriptions/{subscriptionsid}/resourceGroups/{resourceGroupsname}/providers/Microsoft.Sql/servers/{servername}/databases?api-version={apiversion}
public static List<String> getSqlDatabaseList()
{
//System.Diagnostics.Debug.WriteLine("Starting to get database list");
List<string> dbNameList = new List<string>();
string tenantId = "yourtenantid";
string clientId = "yourclientId";
string clientSecret = "clientSecret";
string subscriptionid = "subscriptionid";
string resourcegroup = "resourcegroupname";
string sqlservername = "brandotest";
string version = "2014-04-01";
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientSecret);
var result = authenticationContext.AcquireToken(resource: "https://management.azure.com/", clientCredential: credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Sql/servers/{2}/databases?api-version={3}", subscriptionid, resourcegroup, sqlservername, version));
request.Method = "GET";
request.Headers["Authorization"] = "Bearer " + token;
request.ContentType = "application/json";
var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
string jsonResponse = streamReader.ReadToEnd();
dynamic json = JsonConvert.DeserializeObject(jsonResponse);
dynamic resultList = json.value.Children();
foreach (var item in resultList)
{
dbNameList.Add(((Newtonsoft.Json.Linq.JValue)item.name).Value.ToString());
}
}
return dbNameList;
}
Result:
Another workaround.
I suggest you could use thread.join to wait the list method execute completely.
Code:
public static async Task<List<String>> getSqlDatabaseList()
{
//System.Diagnostics.Debug.WriteLine("Starting to get database list");
List<string> dbNameList = new List<string>();
var credentials = SdkContext.AzureCredentialsFactory.FromFile(#"D:\Auth.txt");
var azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithDefaultSubscription();
var sqlServer = await azure.SqlServers.GetByResourceGroupAsync("brandosecondtest", "brandotest");
IReadOnlyList<ISqlDatabase> dbList = null;
Thread thread = new Thread(() => { dbList = sqlServer.Databases.List(); });
thread.Start();
//wait the thread
thread.Join();
foreach (ISqlDatabase db in dbList)
{
dbNameList.Add(db.Name);
}
return dbNameList;
}
Result:
I'll start off by publishing the code that is troubled:
public async Task main()
{
Task t = func();
await t;
list.ItemsSource = jlist; //jlist previously defined
}
public async Task func()
{
TwitterService service = new TwitterService(_consumerKey, _consumerSecret);
service.AuthenticateWith(_accessToken, _accessTokenSecret);
TwitterGeoLocationSearch g = new TwitterGeoLocationSearch(40.758367, -73.982706, 25, 0);
SearchOptions s = new SearchOptions();
s.Geocode = g;
s.Q = "";
s.Count = 1;
service.Search(s, (statuses, response) => get_tweets(statuses, response));
void get_tweets(TwitterSearchResult statuses, TwitterResponse response)
{
//unimportant code
jlist.Add(info);
System.Diagnostics.Debug.WriteLine("done with get_tweets, jlist created");
}
I am having issues with the get_tweets(..) function running (on what I believe a different thread) and the Task t is not awaited like I have in the main function. Basically, my issue is that the list.Itemsource = jlist is ran before the get_tweets function is finished. Does anyone have a solution or the right direction to point me in?
First, create a TAP wrapper for TwitterService.Search, using TaskCompletionSource. So something like:
public static Task<Tuple<TwitterSearchResult, TwitterResponse>> SearchAsync(this TwitterService service, SearchOptions options)
{
var tcs = new TaskCompletionSource<Tuple<TwitterSearchResult, TwitterResponse>>();
service.Search(options, (status, response) => tcs.SetResult(Tuple.Create(status, response)));
return tcs.Task;
}
Then you can consume it using await:
SearchOptions s = new SearchOptions();
s.Geocode = g;
s.Q = "";
s.Count = 1;
var result = await service.SearchAsync(s);
get_tweets(result.Item1, result.Item2);