csharp-sparkpost
csharp-sparkpost copied to clipboard
RuntimeBinderException
I am receiving a number of RuntimeBinderExceptions upon sending an e-mail via "Transmission.Send(...)":
Exception thrown: 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' in Microsoft.CSharp.dll Additional information: Newtonsoft.Json.Linq.JObject does not contain a definition for results. Additional information: Newtonsoft.Json.Linq.JObject does not contain a definition for id. Additional information: Newtonsoft.Json.Linq.JObject does not contain a definition for total_accepted_recipients. Additional information: Newtonsoft.Json.Linq.JObject does not contain a definition for total_rejected_recipients.
Additional information: The type Newtonsoft.Json.Linq.JValue cannot be converted to a string implicitely (missing cast?)
I'm using the latest SparkPost version (1.1.1) and Newtonsoft.Json 8.0.3. Despite the exceptions being raised, sending the e-mails works as expected. Any suggestions?
Can you send a code sample? Just remove your API key.
// Create API client
var client = new Client(apiKey);
// Construct transmission object
var transmission = new Transmission();
transmission.Content.TemplateId = "templateId";
transmission.Content.Subject = "Subject";
transmission.Recipients.Add(new Recipient { Address = new Address { Email = receiver } });
transmission.SubstitutionData["historyPositions"] = items;
transmission.SubstitutionData["isSingleItem"] = numItems == 1;
transmission.SubstitutionData["singleItemText"] = Strings.NotificationSingleItemChanged;
try
{
await client.Transmissions.Send(transmission).WithCurrentCulture();
return true;
}
catch (ResponseException)
{
return false;
}
Items is a collection of such objects:
public class HistoryItemElement
{
public string CreationDate { get; set; }
public string Field { get; set; }
public string HistoryType { get; set; }
public bool IsNewItem { get; set; }
public bool IsPosition { get; set; }
public bool IsPositionHeader { get; set; }
public string NewValue { get; set; }
public string Number { get; set; }
public string OldValue { get; set; }
public string Project { get; set; }
public string Source { get; set; }
public string User { get; set; }
}
With this line:
await client.Transmissions.Send(transmission).WithCurrentCulture();
I'm not familiar with this method on Task<T>... what is it meant to do?
If I comment out that method call and run it as-is (but changing my template to one that I know works), the email is fired, I get a success message, and it appears in my inbox.
Same exception here!
[Microsoft.CSharp.RuntimeBinder.RuntimeBinderException] = {"'Newtonsoft.Json.Linq.JObject' does not contain a definition for 'results'"}
My code (using SparkPost 1.3.0 and Newtonsoft.Json 6.0.5) :
client.CustomSettings.SendingMode = SendingModes.Sync;
client.Transmissions.Send(trasmission).Wait(); //--> KO: Email fired but RuntimeBinderException exception returns
//client.Transmissions.Send(trasmission); //--> OK: Email fired, no exception returned (nor catched)
Looking at Trasmissions.cs:
public async Task<SendTransmissionResponse> Send(Transmission transmission)
{
var request = new Request
{
Url = $"api/{client.Version}/transmissions",
Method = "POST",
Data = dataMapper.ToDictionary(transmission)
};
var response = await requestSender.Send(request);
if (response.StatusCode != HttpStatusCode.OK) throw new ResponseException(response);
var results = JsonConvert.DeserializeObject<dynamic>(response.Content).results;
return new SendTransmissionResponse()
{
Id = results.id,
ReasonPhrase = response.ReasonPhrase,
StatusCode = response.StatusCode,
Content = response.Content,
TotalAcceptedRecipients = results.total_accepted_recipients,
TotalRejectedRecipients = results.total_rejected_recipients,
};
}
Does "JsonConvert.DeserializeObject
@if2m

Can you try including the SparkPost code as a separate project in your solution, and then debug it to try to get the answer?
Plus, I just released 1.3.0, with sync support. Do you want to try going with sync mode to see if that resolves your problem? Instructions below:
https://github.com/SparkPost/csharp-sparkpost#special-note-about-async
The web call is not causing the problem, I agree. JsonConvert.DeserializeObject could be: property ".results" resolving at runtime may cause the exception?
@if2m It could be. I'm assuming that a 200 response from SparkPost will return that information, but what if it doesn't?
I'll tell you what: We can put some additional checks around it. I'll continue to report back what I got as Content
Issue found: the Json DLL I included was compiled for .NET 3.5 and did not support "dynamic". This code works:
public async Task<SendTransmissionResponse> Send(Transmission transmission)
{
var request = new Request
{
Url = $"api/{client.Version}/transmissions",
Method = "POST",
Data = dataMapper.ToDictionary(transmission)
};
var response = await requestSender.Send(request);
if (response.StatusCode != HttpStatusCode.OK) throw new ResponseException(response);
/*
//Works with JSON.NET 6.0.3 (.NET 4.5 version)
var results = JsonConvert.DeserializeObject<dynamic>(response.Content).results;
return new SendTransmissionResponse()
{
Id = results.id,
ReasonPhrase = response.ReasonPhrase,
StatusCode = response.StatusCode,
Content = response.Content,
TotalAcceptedRecipients = results.total_accepted_recipients,
TotalRejectedRecipients = results.total_rejected_recipients,
};
*/
//Works with JSON.NET (.NET 3.5 version)
var results = JsonConvert.DeserializeObject<dynamic>(response.Content)["results"];
return new SendTransmissionResponse()
{
Id = results["id"].Value,
ReasonPhrase = response.ReasonPhrase,
StatusCode = response.StatusCode,
Content = response.Content,
TotalAcceptedRecipients = (int) results["total_accepted_recipients"].Value, //(int) long
TotalRejectedRecipients = (int) results["total_rejected_recipients"].Value, //(int) long
};
}
See: http://stackoverflow.com/questions/13683757/newtonsoft-json-dynamic-objects
Ah, that's awesome, @if2m , thank you very much!
I wonder... perhaps I could switch this code to deserialize to a string object dictionary? It's something I was considering, anyway, as they seem to be easier to manage than the dynamic. I want to get the receiving of data to work via conventions as well, as lines like this are just begging to be removed:
TotalAcceptedRecipients = results.total_accepted_recipients
TotalAcceptedRecipients = (int) results["total_accepted_recipients"].Value
The DataMapper class alreday converts TotalAcceptedRecipients to total_accepted_recipients, and I want a way to go the other way.
Thank you for passing this along, I'll take a deeper look at it this week and see what I can do.