Accessing Result property of a Task - c#

I am trying to retrieve the ID of an album using the C# Facebook SDK. However I am getting the below error:
System.Threading.Tasks.Task' does not contain a definition for 'Result' and no extension method 'Result' accepting a first argument of type 'System.Threading.Tasks.Task' could be found
Please see the below code, the error occurs on the foreach line
try
{
string wallAlbumID = string.Empty;
FacebookClient client = new FacebookClient(accessToken);
client.GetTaskAsync(pageID + "/albums")
.ContinueWith(task =>
{
if (!task.IsFaulted)
{
foreach (dynamic album in task.Result.data)
{
if (album["type"] == "wall")
{
wallAlbumID = album["id"].ToString();
}
}
}
else
{
throw new DataRetrievalException("Failed to retrieve wall album ID.", task.Exception.InnerException);
}
});
return wallAlbumID;
}
For the record, the FacebookClient.GetTaskAsync method returns Task<object>

I don't know the Facebook API, but the error seems to indicate, that you're dealing with Task class (non-generic) which does not have Result property. It is the generic Task<T> derived from non-generic Task class that has the property. They both allow to run code asynchronously, but the generic class is able to run methods that return values.
If GetTaskAsync returns Task and not Task<T>, then it means you can't get the result from it, as the operation it runs in the background does not return anything.

When I compile your code, I get two errors, the first one is the one you mentioned, and the second one is:
'object' does not contain a definition for 'data' and no extension method 'data' accepting a first argument of type 'object' could be found
This second error is your actual error: task.Result is an object, but (I assume) you want to treat it as dynamic. Because of this error, the compiler also tries to use the overload of ContinueWith() that uses just Task, not Task<object>, which is why you're also getting the first error.
To fix this error, you should cast task.Result to dynamic:
dynamic result = task.Result;
foreach (dynamic album in result.data)
This will compile fine, but it won't actually work, because you set the local variable after you return from the enclosing method.
If you're using C# 5.0, you should use await here, instead of ContinueWith():
try
{
dynamic result = await client.GetTaskAsync(pageID + "/albums");
foreach (dynamic album in result.data)
{
if (album["type"] == "wall")
{
return (string)album["id"].ToString();
}
}
return string.Empty;
}
catch (Exception e) // you should use a specific exception here, but I'm not sure which
{
throw new DataRetrievalException("Failed to retrieve wall album ID.", e);
}
If you can't use C# 5.0, then your whole method should return a Task<string> that's returned by ContinueWith():
return client.GetTaskAsync(pageID + "/albums")
.ContinueWith(
task =>
{
if (!task.IsFaulted)
{
dynamic result = task.Result;
foreach (dynamic album in result.data)
{
if (album["type"] == "wall")
{
return (string)album["id"].ToString();
}
}
return string.Empty;
}
else
{
throw new DataRetrievalException(
"Failed to retrieve wall album ID.", task.Exception.InnerException);
}
});

Related

"Object reference not set to an instance of an object" on Retrieving data

I have tried to retrieve data from SQL database. I am using Entity Framework core. Its retrieving the required data from the database. I can see the data coming when debugging but the data is not assigning to the variable of type var. FYI, the value of variable type is 0 and its basically an enum, i typecast it to int. Below is the code
public async Task<string> GetMailTemplateByType(Models.TemplateTypes type)
{
var mailTemplate = await _userDbContext.MailTemplates.FirstOrDefaultAsync(mt => mt.TemplateType==((int)type));
return mailTemplate.MailHtml;
}
Here is the definition:
var HtmlTemplate = await _coreDataManager.GetMailTemplateByType(TemplateTypes.Activation);
when debug with try catch, Its showing
Object reference not set to an instance of an object
what is the problem here?
We can see from your code that you recieve the following mail template object:
Id = {aeced541-7003-437e-8f77-4605766fb62c};
MailHtml = "Hi, Thank you so much for signing up.Here is Confirmation link to proceed further ...";
TemplateType = 0;
Here you are passing some TemplateType value we don't know
public async Task<string> GetMailTemplateByType(Models.TemplateTypes type)
{
Here you compare that type value to the TemplateType property in the MailTemplate object we see in the dubugger window
var mailTemplate = await _userDbContext.MailTemplates.FirstOrDefaultAsync(mt => mt.TemplateType==((int)type));
But if type is not 0, it will not return the MailTemplate object as the MailTemplate object we see in the debugger window has a TemplateType value of 0, thus FirstOrDefaultAsync will return a null value, see "fault returns NullReferenceException if no match is found"
public async Task<string> GetMailTemplateByType(Models.TemplateTypes type)
{
var mailTemplate = /*your expression from screenshot*/.FirstOrDefault();
if(mailTemplate = null)
throw new NullReferenceException();
return mailTemplate;
}
..........................
try
{
GetMailTemplateByType(TemplateTypesVariable);
}
catch(NullReferenceException err)
{
Console.WriteLine("template does not exist");
}
It looks like you are trying to receive data which does not exist.
Why do you even select the whole object? ef-core is just like sql, select what you need (in your case just do a
var mailTemplate = await _userDbContext.MailTemplates.Where(mt => mt.TemplateType==((int)type)).Select(x => x.MailHtml).FirstOrDefaultAsync();
but this will still not work, since your entity says that the TemplateType is 0 (and your enum starts with 1). Guessing you saved it wrong
wanted to write this as a comment but i just created this account

Make function more Generic to save repeating

I use the following function which is all well and fine but i basically do the same operation about 20 times. For various end points of an api I am hitting how would one make this routing more Generic in the ability to pass and return type OF T.
public async Task<List<StockItem>> GetStockDataFromSage()
{
StockItem stockitems = new StockItem();
string content = "";
List<StockItem> result = new List<StockItem>();
var uri = new Uri(string.Format(Constants.GetStockItems, string.Empty));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<StockItem>>(content);
}
return result;
}
Edit 1
I am trying to use the below however I am getting an error
public async Task<List<StockItem>> GetStockItemInfo()
{
return await dataTransfer.GetDataFromSageService(Constants.GetStockItems, string.Empty)) ?? new List<StockItem>();
}
Severity Code Description Project File Line Suppression State
Error CS1061 'StockTakeDT' does not contain a definition for 'GetStockDataFromSage' and no accessible extension method 'GetStockDataFromSage' accepting a first argument of type 'StockTakeDT' could be found (are you missing a using directive or an assembly reference?) StockAppDL D:\Git\Repos\StockApp\FStockApp\StockAppDal\StockDatabase.cs 76 Active
Your objective here appears to be to call an endpoint and get the results back into an object you can use. If the call is successful, you return the result and if it fails, you return an empty list.
We can abstract that logic out into a generic method that accepts a url and parameters and returns an object.
public async Task<T> GetObjectFromEndpoint<T>(string url, params string[] args)
where T : class
{
var uri = new Uri(string.Format(url, args));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(content);
}
return default(T);
}
Now your GetStockDataFromSage function passes in the information unique to this call, namely the url, parameters, and generic type for the results. If the result is null, GetStockDataFromSage returns an empty list of StockItems
public async Task<List<StockItem>> GetStockDataFromSage()
{
return (await GetObjectFromEndpoint<List<StockItem>>(Constants.GetStockItems, string.Empty)) ?? new List<StockItem>();
}
Any time you are trying to minimize repetition, you want to look at what is specific to this call and what is more general. i.e List<StockItem>, the url, and possibly the parameter are unique, but the rest of the code is very general.
Caution: This method of returning a default value when the api call fails can lead to hard-to-diagnose issues where you will be unable to differentiate between an empty list and a failed api call. I recommend adding some logging for failed api calls and perhaps looking at ways to inform the calling code that the result was in error.

C# - Using reflection with JsonSchema4.FromTypeAsync

[INTRO]
I know there are about a zillion QA about generics and reflections everywhere, but it's becoming a blackhole to me, and I'm only getting more lost the more I read!!
What i need to do is simple, and I'm amazed that it hasn't been addressed before.
[SAMPLE] Consider the following snippit:
public async Task<string> generateJsonSchema(string model)
{
try
{
string modelName = "Models." + model;
Type t = Type.GetType(modelName, false);
JsonSchema4 schema = await JsonSchema4.FromTypeAsync<t>();
return schema.ToJson();
}
catch (Exception ex)
{
Logger.WriteToLogFile(ex.ToString(), "exception");
return "";
}
}
[PROBLEM] Now the main problem is that variable t is evaluated at runtime, thus, JsonSchema4.FromTypeAsync<t>() throws the error 't' is a variable but is used like a type when trying to build compile time
Whoever used JsonSchema4 will understand what I'm trying to achieve here.
Instead of creating a generate function for each of my models, or make a switch/if-else logic,
[QUESTION]
How to make it receive the model name as a string parameter, and convert the string-model-name to model-type and pass it to jSonSchema4 method.
The problem here is that, as you say, t is evaluated as runtime.
I also ran into this Problem and solved it by creating a MethodInfo of the method I wanted to invoke, in your case JsonSchema4.FromTypeAsync<t>().
So basically this is want may fix the problem:
var methodInfo = typeof(JsonSchema4).GetMethod("FromTypeAsync", new Type[] { }); //Get the "normal method info", the overload without parameters
var methodInfoWithType = methodInfo.MakeGenericMethod(t); //now you have a method with your desired parameter t as TypeParameter
Task<JsonSchema4> task = methodInfoWithType.Invoke(null, null) as Task<JsonSchema4>; //now you can cast the result from invoke as Task to keep the Async-Await functionality
var schema = await task;
return schema.ToJson();

Type casting error while using couchbase

I have a view named "TotalPosts" in my couchbase data bucket. The design document name is "Statistics".
This is my code. I am establishing the connection over here..
protected void Page_Load(object sender, EventArgs e)
{
try
{
var client= new CouchbaseClient();
Tasks objTasks = new Tasks();
foreach (Newtonsoft.Json.Linq.JToken jt in objTasks.GetAllStatistics())
{
if (jt["key"].ToString() == "Total")
{
dt.Rows[0]["Value"] = jt["value"].ToString();
}
if (jt["key"].ToString() == "Open")
{
dt.Rows[1]["Value"] = jt["value"].ToString();
}
if (jt["key"].ToString() == "Wah")
{
dt.Rows[2]["Value"] = jt["value"].ToString();
}
}
This is my GetAllStatistics function definition code:
public IEnumerable<Newtonsoft.Json.Linq.JToken> GetAllStatistics()
{
var results = oCouchbase.GetView("Statistics", "TotalPosts");
return results;// I am getting the following error in this line
}
Cannot implicitly convert type 'Couchbase.IView' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)
This is my map code:
function(doc) {
emit("Total", 1);
emit("TotalParticipants", doc.participants.length);
if(doc.status == "1"){
emit("Open", 1);
} else if(doc.status == "2") {
emit("Wah", parseInt(doc.wah_points));
}
}
This is my reduce code:
function (key, values, rereduce) {
return sum(values);
}
I could not find an appropriate type casting. Kindly help me in this issue
I can't claim any knowledge of Couchbase, but the error message indicates that you can perform an explicit cast. Have you tried just doing a cast for your return?
return (Newtonsoft.Json.Linq.JToken)results;
The non-generic version of GetView returns an enumerable list of IViewRow. So instead, I would recommend changing your GetAllStatistics method to something like this:
public IEnumerable<Newtonsoft.Json.Linq.JToken> GetAllStatistics()
{
var results = oCouchbase.GetView("Statistics", "TotalPosts");
foreach(var row in results)
{
yield return oCouchbase.Get<Newtonsoft.Json.Linq.JToken>(row.ItemId);
}
}
Then your foreach loop should work as is. However, a question I have is what your documents look like. Are you serializing JToken instances as the value in your Store operations? If you are, then they will not be available in your views as only valid JSON strings will be usable within a view. If you have any questions about anything I just mentioned, please feel free to edit your question to include your Store operations and the map/reduce view code.

Multithreading with Async gives a TypeCasting Error when using TaskEx.WhenAll()

In the code below, I am trying to execute a method, which returns a value, in another thread. However, it just DOES NOT work!!!
public void main()
{
lstChapters.DataContext = await TaskEx.WhenAll(LoadChapters());
}
//CAN'T use async in this function, it requires Task<> which
//Error appears on the code inside []
public [async Task<object>] Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
dictChapters data = await IQ_LoadQuranXML.LoadChapters(TypeIndex);
}
internal static async Task< IEnumerable<dictChapters>> LoadChapters()
{
var element = XElement.Load("xml/chapters.xml");
Task < IEnumerable < dictChapters >> something = (Task<IEnumerable<dictChapters>>) await TaskEx.Run(delegate
{
IEnumerable<dictChapters> Chapters =
from var in element.Descendants("chapter")
orderby var.Attribute("index").Value
select new dictChapters
{
ChapterIndex = Convert.ToInt32(var.Attribute("index").Value),
ChapterArabicName = var.Attribute("name").Value,
ChapterType = var.Attribute("type").Value,
};
return Chapters;}
);
return something; //An ERROR on this line
}
//Overriding method which does not return IEnumerable type. And it accepts index as integer.
internal static dictChapters LoadChapters(string chIdx = "0")
{
int chIdxInt = Convert.ToInt32(chIdx);
List<dictChapters> Chapters = (List<dictChapters>) LoadChapters(); // ERROR is on this line too
return Chapters.ElementAt(chIdxInt - 1); //index of chapter in the element starts from 0
}
The Error is:
Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<iq_main.dictChapters>>' to 'System.Collections.Generic.IEnumerable<iq_main.dictChapters>'. An explicit conversion exists (are you missing a cast?)
And the Other error is..
Cannot convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List<iq_main.dictChapters>>' to 'System.Collections.Generic.List<iq_main.dictChapters>
When I cast "something" explicitly like return (IEnumerable<dictChapters>) something then at runtime, I get "InvalidCastException".
Actually, you'll be getting a runtime cast error earlier than that. The problem is your cast of the TaskEx.Run result. When you await something, the Task wrapper is removed.
public void main()
{
lstChapters.DataContext = await LoadChapters();
}
internal static Task<List<dictChapters>> LoadChapters()
{
return TaskEx.Run(delegate
{
var element = XElement.Load("xml/chapters.xml");
IEnumerable<dictChapters> Chapters =
from var in element.Descendants("chapter")
orderby var.Attribute("index").Value
select new dictChapters
{
ChapterIndex = Convert.ToInt32(var.Attribute("index").Value),
ChapterArabicName = var.Attribute("name").Value,
ChapterType = var.Attribute("type").Value,
};
return Chapters.ToList();
});
}
There are a few other problems with your code as well. Remember that enumerations like this are lazily executed. You probably want to return Chapters.ToList(); so that the XML parsing happens on the thread pool thread.
Since you did await on the TaskEx.Run, you have the enumerable coming back, not a task.
For what you're doing, I'd recommend just keeping LoadChapters as normal/sync code, then either just invoke it via Task.Run, or call it as-is.
Due to deferred execution, AFAICT your current code doesn't really help anything since you're still doing the Load synchronously.
The Task.WhenAll in main could be removed, just await LoadChapters (or whatever the asynchronous method is

Categories