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.
Related
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());
I'm using the swagger-codegen to generate c# client, however noticing that the sortParamsByRequiredFlag is not applied to model generation.
For example, here is a sample config file:
{
"packageVersion" : "1.0.0",
"sortParamsByRequiredFlag": true,
"optionalProjectFile" : false
}
Here is the generated truncated code for model's constructor:
/// <summary>
/// Initializes a new instance of the <see cref="V2alpha1CronJobSpec" /> class.
/// </summary>
/// <param name="ConcurrencyPolicy">Specifies how to treat concurrent executions of a Job. Defaults to Allow..</param>
/// <param name="FailedJobsHistoryLimit">The number of failed finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified..</param>
/// <param name="JobTemplate">Specifies the job that will be created when executing a CronJob. (required).</param>
/// <param name="Schedule">The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. (required).</param>
/// <param name="StartingDeadlineSeconds">Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones..</param>
/// <param name="SuccessfulJobsHistoryLimit">The number of successful finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified..</param>
/// <param name="Suspend">This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false..</param>
public V2alpha1CronJobSpec(string ConcurrencyPolicy = default(string), int? FailedJobsHistoryLimit = default(int?), V2alpha1JobTemplateSpec JobTemplate = default(V2alpha1JobTemplateSpec), string Schedule = default(string), long? StartingDeadlineSeconds = default(long?), int? SuccessfulJobsHistoryLimit = default(int?), bool? Suspend = default(bool?))
{
// to ensure "JobTemplate" is required (not null)
if (JobTemplate == null)
{
throw new InvalidDataException("JobTemplate is a required property for V2alpha1CronJobSpec and cannot be null");
}
else
{
this.JobTemplate = JobTemplate;
}
// to ensure "Schedule" is required (not null)
if (Schedule == null)
{
throw new InvalidDataException("Schedule is a required property for V2alpha1CronJobSpec and cannot be null");
}
else
{
this.Schedule = Schedule;
}
this.ConcurrencyPolicy = ConcurrencyPolicy;
this.FailedJobsHistoryLimit = FailedJobsHistoryLimit;
this.StartingDeadlineSeconds = StartingDeadlineSeconds;
this.SuccessfulJobsHistoryLimit = SuccessfulJobsHistoryLimit;
this.Suspend = Suspend;
}
As you can see from the swagger spec, JobTemplate, Schedule are required parameters. However, the params in the constructor are sorted alphabetically.
I've been sorting through the swagger-codegen code base and I think the sortParamsByRequiredFlag only applies to API generated methods.
Is this by design? I'm not sure if I'm missing some config that I should be setting?
Here is the GitHub issue I opened but haven't heard anything on it.
This is not an answer to why Swagger is not generating the correct code, but an answer to your problem on GitHub.
There is absolutely no need for you to use this:
var jobSpec = new V2alpha1CronJobSpec(null, null, new V2alpha1JobTemplateSpec(), "stringValue", null, ...);
You can just use this (called named parameters):
var jobSpec = new V2alpha1CronJobSpec(JobTemplate: new V2alpha1JobTemplateSpec(), Schedule: "stringValue");
For c# client this flag is ignored in the main codebase.
You can create your own extension and override fromModel method to sort the properties.
Have a look at below code on how to do it.
#Override
public CodegenModel fromModel(String name, Model model, Map<String, Model> allDefinitions) {
CodegenModel codegenModel = super.fromModel(name, model, allDefinitions);
if (sortParamsByRequiredFlag)
{
Collections.sort(codegenModel.readWriteVars, new Comparator<CodegenProperty>() {
#Override
public int compare(CodegenProperty one, CodegenProperty another) {
if (one.required == another.required) return 0;
else if (one.required) return -1;
else return 1;
}
});
System.out.println("***Sorting based on required params in model....***");
}
return codegenModel;
}
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.
A pure method is defined as "does not make any visible state changes".
My method is writing a log message if one argument is null or an exception is thrown. Is it still pure? Is writing to a logger a visible change?
Here's the code:
/// <summary>
/// Formats with invariant culture - won't throw an exception.
/// </summary>
/// <param name="format">A composite format string.</param>
/// <param name="args">An object array that contains zero or more objects to format.</param>
/// <returns>A copy of format in which the format items have been replaced by the string representation of the corresponding objects in args.</returns>
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "CyanCor.Core.Logging.Log.ExceptionPrevent(System.String,System.String,System.Int32,System.String)", Justification = "It is for the logger (riedl)")]
[Pure - yes or no?]
public static string FormatSafe(this string format, params object[] args)
{
if (format == null)
{
Log.ExceptionPrevent("Argument format is null");
return NullFormat;
}
try
{
return string.Format(CultureInfo.InvariantCulture, format, args);
}
catch (ArgumentException exc)
{
Log.Exception(exc);
return format;
}
catch (FormatException exc)
{
Log.Exception(exc);
return format;
}
}
Generally, "state change" means modifying the state of an object, ie. modifying a variable or changing the structure of a complex object. Following this definition, it would seem that your method is still pure.
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.