How to use unirest-net properly on Discord - c#

I want to ReplyAsync lyrics for a particular song so I used the unirest-net api. Here's how I use the command:
/// <summary> The lyric generating task </summary>
/// <returns> The <see cref="Task"/> </returns>
[Command("lyric")]
public async Task LyricTask()
{
Task<HttpResponse<string>> response = Unirest.get("https://musixmatchcom-musixmatch.p.mashape.com/wsr/1.1/matcher.lyrics.get?q_artist=imagine+dragons&q_track=Battle+cry")
.header("X-Mashape-Key", "secret_key")
.header("Accept", "application/json")
.asJsonAsync<string>();
await this.ReplyAsync(response.ToString());
}
But I get the output System.Threading.Tasks.Task1[unirest_net.http.HttpResponse1[System.String]] always. What am i doing wrong and how do i make it work?

I haven't used HttpResponse before but after looking at the documentation, I think you need to call Flush() instead of ToString().
From the Flush() documentation:
Sends all currently buffered output to the client.
Edit: You have to call Flush() from the Result property of the Task<HttpResult> object:
await this.ReplyAsync(response.Result.Flush());

Related

Attach to previously started async process

So i have an app that works using a token for a user. User can be signed in only on 1 device ( after login previous tokens get expired ). So i came up with an idea to cache some data. So i created a CacheManager that is a singleton.
CacheManager has a Dictionary with previously fetched data in it.
So here is an example:
/// <summary>
/// Tries to get Global settings data from the cache. If it is not present - asks ServiceManager to fetch it.
/// </summary>
/// <returns>Global setting object</returns>
public async Task<GlobalSettingsModel> GetGlobalSettingAsync()
{
GlobalSettingsModel result;
if (!this.cache.ContainsKey("GlobalSettings"))
{
result = await ServiceManager.Instance.RequestGlobalSettingAsync();
if (result != null)
{
this.cache.Add("GlobalSettings", result);
}
// TODO: Figure out what to do in case of null
}
return (GlobalSettingsModel)this.cache["GlobalSettings"];
}
So the question is, how can i modify this method, to handle such case:
For example, method that i call from the server, works longer than user navigated to the page where the data is needed i want to show a loading indicator and hide it when the data has actually been received.
Why do i need it, we have 2 pages - ExtendedSplashScreen and UpdatesPage user can quickly skip them ( 1s ) or stay and read interesting info ( lets say 1m ).
In this time, i have started to fetch the GetGlobalSetting in order to have the process ended or download atleast something ( to minify wait for the user ) when he gets to LoginPage.
On my ExtendedSplashScreen i launched:
CacheManager.Instance.GetGlobalSettingAsync();
For test purposes, i have modified the ServiceManager method:
/// <summary>
/// Fetches the object of Global Settings from the server
/// </summary>
/// <returns>Global setting object</returns>
public async Task<GlobalSettingsModel> RequestGlobalSettingAsync()
{
await Task.Delay(60000);
// Request and response JSONs are here, because we will need them to be logged if any unexpected exceptions will occur
// Response JSON
string responseData = string.Empty;
// Request JSON
string requestData = JsonConvert.SerializeObject(new GlobalSettingsRequestModel());
// Posting list of keys that we want to get from GlobalSettings table
HttpResponseMessage response = await client.PostAsync("ServerMethod", new StringContent(requestData, Encoding.UTF8, "application/json"));
// TODO: HANDLE ALL THE SERVER POSSIBLE ERRORS
Stream receiveStream = await response.Content.ReadAsStreamAsync();
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
// Read the response data
responseData = readStream.ReadToEnd();
return JsonConvert.DeserializeObject<GlobalSettingsResponseModel>(responseData).GlobalSettings;
}
So, when user gets to the LoginPage i do the:
// The await is here because there is no way without this data further
GlobalSettingsModel settings = await CacheManager.Instance.GetGlobalSettingAsync();
And here, i would like to get the data from the cache if it was already downloaded or that CacheManager would return me the data as soon as it will be finished downloading.
One way would be to cache the Task<GlobalSettingsModel> instead of the GlobalSettingsModel itself. When you obtain it from the cache, you could check if it has completed and then either await or use its result accordingly.

Mocking HttpResponse WriteAsync

I'm trying to get a call to WriteAsync mocked on a mockHttpResponse and I can't figure out the syntax to use.
var responseMock = new Mock<HttpResponse>();
responseMock.Setup(x => x.WriteAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()));
ctx.Setup(x => x.Response).Returns(responseMock.Object);
The test bombs with the following error:
System.NotSupportedException : Invalid setup on an extension method: x
=> x.WriteAsync(It.IsAny(), It.IsAny())
Ultimately I want to verify the correct string has been written to the response.
How to correctly set this up?
Here's a solution that seems to work in .NET Core 3.1, for completeness:
const string expectedResponseText = "I see your schwartz is as big as mine!";
DefaultHttpContext httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
// Whatever your test needs to do
httpContext.Response.Body.Position = 0;
using (StreamReader streamReader = new StreamReader(httpContext.Response.Body))
{
string actualResponseText = await streamReader.ReadToEndAsync();
Assert.Equal(expectedResponseText, actualResponseText);
}
Moq cannot Setup extension methods. If you know what the extension method accesses then some cases you can mock a safe path through the extension method.
WriteAsync(HttpResponse, String, CancellationToken)
Writes the given text to the response body. UTF-8 encoding will be used.
directly accesses the HttpResponse.Body.WriteAsync where Body is a Stream via the following overload
/// <summary>
/// Writes the given text to the response body using the given encoding.
/// </summary>
/// <param name="response">The <see cref="HttpResponse"/>.</param>
/// <param name="text">The text to write to the response.</param>
/// <param name="encoding">The encoding to use.</param>
/// <param name="cancellationToken">Notifies when request operations should be cancelled.</param>
/// <returns>A task that represents the completion of the write operation.</returns>
public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
byte[] data = encoding.GetBytes(text);
return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);
}
This means you would need mock response.Body.WriteAsync
//Arrange
var expected = "Hello World";
string actual = null;
var responseMock = new Mock<HttpResponse>();
responseMock
.Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(),It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Callback((byte[] data, int offset, int length, CancellationToken token)=> {
if(length > 0)
actual = Encoding.UTF8.GetString(data);
})
.ReturnsAsync();
//...code removed for brevity
//...
Assert.AreEqual(expected, actual);
The callback was used to capture the arguments passed to the mocked member. Its value was stored in a variable to be asserted later in the test.

Mvc .Net Catch Exception from async method

I am using this library as wrapper for Mailchimp API v3.0
Now I am having issue to catch Exceptions from methods of this library.
The methods are throwing MailChimpException which is extended from Exception.
The thing is if I run it from Console APP then I am able to catch exception, but when I run it from my Web project, I cannot catch Exception and the code just stuck when exception ocurred.
Here is example of My call:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = api.Members.AddOrUpdateAsync(listId, profile).Result;
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
And here is the method from the library which I call:
namespace MailChimp.Net.Logic
{
/// <summary>
/// The member logic.
/// </summary>
internal class MemberLogic : BaseLogic, IMemberLogic
{
private const string BaseUrl = "lists";
/// <summary>
/// Initializes a new instance of the <see cref="MemberLogic"/> class.
/// </summary>
/// <param name="apiKey">
/// The api key.
/// </param>
public MemberLogic(string apiKey)
: base(apiKey)
{
}
/// <summary>
/// The add or update async.
/// </summary>
/// <param name="listId">
/// The list id.
/// </param>
/// <param name="member">
/// The member.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref>
/// <name>uriString</name>
/// </paramref>
/// is null. </exception>
/// <exception cref="UriFormatException">In the .NET for Windows Store apps or the Portable Class Library, catch the base class exception, <see cref="T:System.FormatException" />, instead.<paramref name="uriString" /> is empty.-or- The scheme specified in <paramref name="uriString" /> is not correctly formed. See <see cref="M:System.Uri.CheckSchemeName(System.String)" />.-or- <paramref name="uriString" /> contains too many slashes.-or- The password specified in <paramref name="uriString" /> is not valid.-or- The host name specified in <paramref name="uriString" /> is not valid.-or- The file name specified in <paramref name="uriString" /> is not valid. -or- The user name specified in <paramref name="uriString" /> is not valid.-or- The host or authority name specified in <paramref name="uriString" /> cannot be terminated by backslashes.-or- The port number specified in <paramref name="uriString" /> is not valid or cannot be parsed.-or- The length of <paramref name="uriString" /> exceeds 65519 characters.-or- The length of the scheme specified in <paramref name="uriString" /> exceeds 1023 characters.-or- There is an invalid character sequence in <paramref name="uriString" />.-or- The MS-DOS path specified in <paramref name="uriString" /> must start with c:\\.</exception>
/// <exception cref="MailChimpException">
/// Custom Mail Chimp Exception
/// </exception>
/// <exception cref="TargetInvocationException">The algorithm was used with Federal Information Processing Standards (FIPS) mode enabled, but is not FIPS compatible.</exception>
/// <exception cref="ObjectDisposedException">
/// The object has already been disposed.
/// </exception>
/// <exception cref="EncoderFallbackException">
/// A fallback occurred (see Character Encoding in the .NET Framework for complete explanation)-and-<see cref="P:System.Text.Encoding.EncoderFallback"/> is set to <see cref="T:System.Text.EncoderExceptionFallback"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Enlarging the value of this instance would exceed <see cref="P:System.Text.StringBuilder.MaxCapacity"/>.
/// </exception>
/// <exception cref="FormatException">
/// <paramref>
/// <name>format</name>
/// </paramref>
/// includes an unsupported specifier. Supported format specifiers are listed in the Remarks section.
/// </exception>
public async Task<Member> AddOrUpdateAsync(string listId, Member member)
{
using (var client = this.CreateMailClient($"{BaseUrl}/"))
{
var response =
await
client.PutAsJsonAsync($"{listId}/members/{this.Hash(member.EmailAddress.ToLower())}", member, null).ConfigureAwait(false);
await response.EnsureSuccessMailChimpAsync().ConfigureAwait(false);
return await response.Content.ReadAsAsync<Member>().ConfigureAwait(false);
}
}
}
}
Does anyone has idea why I am able to trigger exception in Console APP but not in Web project. Any suggestion what to check?
UPDATE:
If I change my code to use Task.Run like:
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
SO:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
Then I catch Exception in catch block, but then How I can get result from task when everything is fine???
The proper solution is to make this "async all the way", as I describe in my MSDN article on async best practices. The problem with calling Result directly is that it causes a deadlock (as I describe on my blog post Don't block on async code). (In your case, a secondary problem from Result is that it will wrap MailChimpException within an AggregateException, making error handling more awkward).
Wrapping the call to Task.Run and then calling Result is a hack that decreases the scalability of your web service (and doesn't take care of the secondary problem of exception wrapping, either). If you don't want to use asynchronous code, then use a synchronous API. Since this is an I/O operation, the most natural approach is asynchronous code, which would look like this:
public async Task<bool> ListSubscribeAsync(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = await api.Members.AddOrUpdateAsync(listId, profile);
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
This is an answer to the updated question.
You can get the result simply by calling Task.Result on the task which may throw an exception. You lambda expression has to return the called method's Task<T> result using return await. Otherwise the result of the the whole expression will be a non-generic task, which represents an operation that does not return a value and doesn't have a Result member.
static void Main(string[] args)
{
try
{
var task = Task.Run(async () => { return await AsyncMethod(); });
Console.WriteLine("Result: " + task.Result);
}
catch
{
Console.WriteLine("Exception caught!");
}
Console.ReadKey();
}
static async Task<string> AsyncMethod()
{
throw new ArgumentException();
var result = await Task.Run(() => { return "Result from AsyncMethod"; });
return result;
}
Notice that I've removed the call to Task.Wait, because it is unnecessary when you also fetch the result.
Quote from MSDN's Task<TResult>.Result reference page:
Accessing the property's get accessor blocks the calling thread until
the asynchronous operation is complete; it is equivalent to calling
the Wait method.
The code snippet will display the text from catch block. If you remove the first line from AsyncMethod, you will get the result displayed.
This: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
To cut a long story short, you never access the result of your async method, and never see the exception. If you dont want to make the parent method async, then you need to explicitly get the value of the .Result property from the task object returned by AddOrUpdateAsync method.

Is it possible to have a 'finally' code-block for yield-return methods?

Background
Hi, everyone. I have an abstract class called BaseRecordFetcher<TEntity> it has one method that takes read/sort/translate/move-next methods from the child class, and yield returns the results as model entities.
When I read multiple data rows, it correctly does a yield return for each entity, then reaches the Trace message after the do...while without any problems.
Problem
I have however noticed that when I use IEnumerable.FirstOrDefault() on the collection, the system assumes no more yield returns are required, and it does not finish execution of this method!
Other than the trace-output on how many records were returned, I do not have too much of a problem with this behaviour, but it got me thinking: "What if I do need to have some... let's call it finally code?"
Question
Is there a way to always ensure the system runs some post-processing code after yield return?
Code
/// <summary>
/// Retrieve translated entities from the database. The methods used to do
/// this are specified from the child class as parameters (i.e. Action or
/// Func delegates).
/// </summary>
/// <param name="loadSubsetFunc">
/// Specify how to load a set of database records. Return boolean
/// confirmation that records were found.
/// </param>
/// <param name="preIterationAction">
/// Specify what should happen to sort the results.
/// </param>
/// <param name="translateRowFunc">
/// Specify how a database record should translate to a model entity.
/// Return the new entity.
/// </param>
/// <param name="moveNextFunc">
/// Specify how the database row pointer should move on. Return FALSE on a
/// call to the final row.
/// </param>
/// <returns>
/// A set of translated entities from the database.
/// </returns>
/// <example><code>
///
/// return base.FetchRecords(
/// _dOOdad.LoadFacilitySites,
/// () => _dOOdad.Sort = _dOOdad.GetAutoKeyColumn(),
/// () =>
/// {
/// var entity = new FacilitySite();
/// return entity.PopulateLookupEntity(
/// _dOOdad.CurrentRow.ItemArray);
/// },
/// _dOOdad.MoveNext);
///
/// </code></example>
protected virtual IEnumerable<TEntity> FetchRecords(
Func<bool> loadSubsetFunc, Action preIterationAction,
Func<TEntity> translateRowFunc, Func<bool> moveNextFunc)
{
// If records are found, sort them and return set of entities
if (loadSubsetFunc())
{
Trace.WriteLine(string.Format(
"# FOUND one or more records: Returning {0}(s) as a set.",
typeof(TEntity).Name));
int recordCount = 0;
preIterationAction();
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
// This code never gets reached if FirstOrDefault() is used on the set,
// because the system will assume no more enities need to be returned
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
else
{
Trace.WriteLine(string.Format(
"# ZERO records found: Returning an empty set of {0}.",
typeof(TEntity).Name));
}
}
EDIT (added solution; thank you #Servy and #BenRobinson):
try
{
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
}
finally
{
// This code always executes, even when you use FirstOrDefault() on the set.
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
Yes, you can provide finally blocks within an iterator block, and yes, they are designed to handle this exact case.
IEnumerator<T> implements IDisposable. When the compiler transforms the iterator block into an implementation the Dispose method of the enumerator will execute any finally blocks that should be executed based on where the enumerator currently was when it was disposed of.
This means that as long as whoever is iterating the IEnumerator makes sure to always dispose of their enumerators, you can be sure that your finally blocks will run.
Also note that using blocks will be transformed into a try/finally block, and so will work essentially as one would hope within iterator blocks. They will clean up the given resource, if needed, when the iterator is disposed.

What's wrong with TaskCompletionSource<DeploymentCatalog>?

I am using the Nuget package System.Threading.Tasks for Silverlight 4 (a port from Mono). I keep getting the InvalidOperationException ("The underlying Task is already in one of the three final states: RanToCompletion, Faulted, or Canceled.") for the following:
var tasks = new Task<DeploymentCatalog>[2];
//Catalog the XAP downloaded by the Application Loader, the BiggestBox Index:
var uri0 = new Uri(Application.Current.
Host.InitParams["LoaderInfoXapPath"], UriKind.Relative);
tasks[0] = CompositionUtility.DownloadCatalogAsync(uri0);
//Catalog the XAP with the BiggestBox Index Part:
var uri1 = new Uri(Application.Current
.Host.InitParams["BiggestBoxIndexXapPath"], UriKind.Relative);
tasks[1] = CompositionUtility.DownloadCatalogAsync(uri1);
//tasks did not run by default...
//tasks[0].Start();
//tasks[1].Start();
Task.WaitAll(tasks);
this.AddToAggregateCatalog(tasks[0].Result);
this.AddToAggregateCatalog(tasks[1].Result);
base.Compose();
where DownloadCatalogAsync is:
/// <summary>
/// Downloads the catalog
/// in a <see cref="System.Threading.Task"/>.
/// </summary>
/// <param name="location">The location.</param>
/// <param name="downloadCompleteAction">The download complete action.</param>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope",
Justification = "Reliable disposal depends on callers.")]
public static Task<DeploymentCatalog> DownloadCatalogAsync(Uri location)
{
return DownloadCatalogAsTask(location, null);
}
/// <summary>
/// Downloads the catalog
/// in a <see cref="System.Threading.Task"/>.
/// </summary>
/// <param name="location">The location.</param>
/// <param name="downloadCompleteAction">The download complete action.</param>
/// <remarks>
/// For details, see the “Converting an Event-Based Pattern” section in
/// “Simplify Asynchronous Programming with Tasks”
/// by Igor Ostrovsky
/// [http://msdn.microsoft.com/en-us/magazine/ff959203.aspx]
/// </remarks>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope",
Justification = "Reliable disposal depends on callers.")]
public static Task<DeploymentCatalog> DownloadCatalogAsync(Uri location,
Action<object, AsyncCompletedEventArgs> downloadCompleteAction)
{
var completionSource = new TaskCompletionSource<DeploymentCatalog>();
var catalog = new DeploymentCatalog(location);
catalog.DownloadCompleted += (s, args) =>
{
if(args.Error != null) completionSource.SetException(args.Error);
else if(args.Cancelled) completionSource.SetCanceled();
else
{
completionSource.SetResult(s as DeploymentCatalog); //exception thrown here
if(downloadCompleteAction != null)
downloadCompleteAction.Invoke(s, args);
}
};
catalog.DownloadAsync();
return completionSource.Task;
}
I use this same pattern with WebClient and it works fine (but I don't have to Start() the tasks explicitly---however I did not test WebClient with the Mono-ported version of Task Parallel Library (for Silverlight). I guess I should do that...
You're calling Start on the TaskCompletionSource and the TCS Task had already started and, in the case when you get this exception, it already completed too. Generally you want to design methods like DownloadCatalogAsTask to return already started Task instances and callers can expect them to be started and avoid calling Start themselves.
Couple other suggestions:
I would consider renaming the method to DownloadCatalogAsync to align with .NET 4.5 naming guidelines.
I would not pass the Action<> into the async method. The whole point of returning a Task is so that you can chain on continuations using familiar patterns like an explicit ContinueWith or the await keyword in C# 5.0. Here you're inventing you own approach.
I had the same problem. i.e. Mono TPL on Silverlight 4; exception thrown at completionSource.SetResult().
This was resolved when I used completionSource.TrySetResult() instead.

Categories

Resources