I'm getting multiple errors. As I'm new to this async/await process. So with little research I've done this bit:-
I've a function like:-
public async Task<JsonResult> GetMultipleTblResult(AdminBundle aBundleFetch)
{
if (!string.IsNullOrEmpty(aBundleFetch.ListType) && aBundleFetch.ListType.Equals(Constants.Board))
{
ArrayList MainList = new ArrayList();
aBundleFetch.ListType = Constants.Board;
Func<ArrayList> functionBoard = new Func<ArrayList>(() => FetchTableDataAsync(aBundleFetch)); // Getting Error (Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<System.Collections.ArrayList>>' to 'System.Collections.ArrayList')
ArrayList resBoard = await Task.Factory.StartNew<ArrayList>(functionBoard);
aBundleFetch.ListType = Constants.Classes;
Func<ArrayList> functionClass = new Func<ArrayList>(() => FetchTableDataAsync(aBundleFetch)); // Getting Error (Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<System.Collections.ArrayList>>' to 'System.Collections.ArrayList')
ArrayList resClass = await Task.Factory.StartNew<ArrayList>(functionClass);
aBundleFetch.ListType = Constants.ClassSubject;
Func<ArrayList> functionClassSubject = new Func<ArrayList>(() => FetchTableDataAsync(aBundleFetch)); // Getting Error (Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<System.Collections.ArrayList>>' to 'System.Collections.ArrayList')
ArrayList resClassSubject = await Task.Factory.StartNew<ArrayList>(functionClassSubject);
aBundleFetch.ListType = Constants.ClassMaterial;
Func<ArrayList> functionClassMaterial = new Func<ArrayList>(() => FetchTableDataAsync(aBundleFetch)); // Getting Error (Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<System.Collections.ArrayList>>' to 'System.Collections.ArrayList')
ArrayList resClassMaterial = await Task.Factory.StartNew<ArrayList>(functionClassMaterial);
MainList.Add(resBoard);
MainList.Add(resClass);
MainList.Add(resClassSubject);
MainList.Add(resClassMaterial);
var jsonSerialiser = new JavaScriptSerializer();
var json = jsonSerialiser.Serialize(MainList);
return new JsonResult { Data = json, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
return new JsonResult { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
From my FetchTableDataAsync function I want to return a List of arraylists and send them over to GetMultipleTblResult :-
public async Task<IEnumerable<ArrayList>> FetchTableDataAsync(AdminBundle abundleList)
{
AdminBundle abundle = new AdminBundle();
string innerMesage = string.Empty;
if (Session["AdminBundle"] != null)
abundle = (AdminBundle)Session["AdminBundle"];
ArrayList BulkList = null;
abundle.ListType = abundleList.ListType;
if (!string.IsNullOrEmpty(abundleList.ListType))
{
using (SMContext db = new SMContext())
{
switch (abundleList.ListType)
{
case "Category":
List<Category> CategoryList = null;
CategoryList = db.CatObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList();
BulkList.Add(CategoryList);
break;
//Class Starts
case "Board":
List<Board> BoardList = null;
BoardList = db.BoardObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList();
BulkList.Add(BoardList);
break;
default:
break;
//Main default Ends
}
}
}
return await BulkList; //Getting Error 'ArrayList' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'ArrayList' could be found (are you missing a using directive or an assembly reference?)
}
Basically I want to return set of multiple lists asynchronously from later function(FetchTableDataAsync) to previous function(GetMultipleTblResult) and then pass it to my angular.js file in JSON format.
EDIT:
So with help of #JohnWu I've done this bit:-
[HttpPost]
[LogInFilter]
public JsonResult GetMultipleTblResult(AdminBundle aBundleFetch)
{
if (!string.IsNullOrEmpty(aBundleFetch.ListType) && aBundleFetch.ListType.Equals(Constants.Board))
{
Task<AllTblListClass> AllTblObj = GetTableDataAsync(aBundleFetch);
//var jsonSerialiser = new JavaScriptSerializer();
//var json = jsonSerialiser.Serialize(AllTblObj);
return new JsonResult { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
return new JsonResult { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public async Task<AllTblListClass> GetTableDataAsync(AdminBundle abundleList)
{
if (!string.IsNullOrEmpty(abundleList.ListType) && abundleList.ListType.Equals(Constants.Board))
{
return new AllTblListClass
{
BoardObj = await FetchBoardsAsync(),
ClsObj = await FetchClassAsync(),
ClsSubObj = await FetchClassSubAsync(),
MatTypeObj = await FetchMaterialTAsync(),
ClassSubMatRelationObj = await FetchClassSubMatRelAsync()
};
}
else
{
return new AllTblListClass { };
}
}
public async Task<List<ClassSubMatRelation>> FetchClassSubMatRelAsync()
{
using (SMContext db = new SMContext())
{
return await Task<List<ClassSubMatRelation>>.Run(() => db.ClassSubMatRelationObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList()); // It executes untill here and then sleeps for endless time.
}
} //I'm not writing all functions as it will create a long question
But on this line of code:-
return await Task<List<ClassSubMatRelation>>.Run(() => db.ClassSubMatRelationObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList());
The execution sleeps and nothing happens. There isn't any Error or Exception generating.
From the end of your second method:
return await BulkList;
Here, BulkList is declared as ArrayList. There is no need for this method to be async or involve Task<T> in any way, so the most appropriate option is simply to remove all the async and Task from that method. If you need to expose it as a Task<T> - Task.FromResult may be of use, but it is sub-optimal.
It seems you want a single function to return either a list of categories or a list of boards. If boards and categories are not related (e.g. they do not share a common interface), then this is a questionable design. How is the caller going to call it? The caller at some point has to know the difference, and the developer has to know in order to put the list into something type-specific so the objects can be read. If the caller knows the difference anyway, why not use two separate functions? For example
public async Task<IEnumerable<Category>> FetchCategoriesAsync(AdminBundle abundleList)
{
if (abundleList.ListType != "Category") throw new ArgumentException("abundleList");
AdminBundle abundle = Session["AdminBundle"] as AdminBundle;
abundle.ListType = abundleList.ListType;
using (SMContext db = new SMContext())
{
return await Task<List<Category>>.Run( () => db.CatObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList());
}
}
Notice in this example the db call is wrapped in a task and awaited. This will give you the asynchronicity that you are looking for (a method can't act async unless there is an await inside it somewhere).
If you want to be able to get categories and boards at the same time, you can implement a wrapper function on top of it, like this:
class TableData
{
public List<Catgeory> Categories { get; set; }
public List<Boards> Boards { get; set; }
}
public async Task<TableData> GetTableDataAsync(AdminBundle abundleList)
{
return new TableData
{
Categories = await FetchCategoriesAsync(abundleList),
Boards = await FetchBoardsAsync(abundleList);
};
}
Related
What is the best way to add non-Task element to existing Task<List<MyClass>>?
I have to add MyClass element nonTaskClass into result x that is Task List. The thing is that it is not in any way async. It is just some pure conditional logic that results into object. Something like this:
public Task<List<MyClass>> CheckAsync(Input input) {
// Get some class based on sync logic/conditional logic
var nonTaskClass = new MyClass();
if (input.Form == 1 || input.Country = "Lithuania") {
nonTaskClass.Message = "Some message goes here";
} else {
nonTaskClass.Message = "Another message";
}
// Create tasks (calls different methods/services and get data)
// Task list actually is based on "nonTaskClass"
// Get data from tasks and return
var x = Task.WhenAll(tasks).ContinueWith(t =>
{
return t.Result.Select(o => new MyClass
{
Message = o.Message
}).ToList();
});
// Need to add nonTaskClass into x, how?!!!!!
return x;
}
If it wasn't feasible to refactor the method - including making it async - I would add Task.FromResult(nonTaskClass) to the list of tasks:
tasks.Add(Task.FromResult(nonTaskClass));
// Get data from tasks and return
var x = Task.WhenAll(tasks)...
I would make it async and change it to:
public async Task<List<MyClass>> CheckAsync(Input input)
{
var nonTaskClass = new MyClass();
if (input.Form == 1 || input.Country = "Lithuania")
{
nonTaskClass.Message = "Some message goes here";
}
else
{
nonTaskClass.Message = "Another message";
}
var tasks = GetSomeTasks();
return (await Task.WhenAll(tasks))
.Select(x => new MyClass { Message = x.Message })
.Append(nonTaskClass)
.ToList();
}
I have a class as follows:
class Promise<T> {
Promise<R> Then<R>(Func<T,R> handler){
var result = new Promise<R>();
this.Then((value) => {
result.Complete(handler.Invoke(value));
});
this.Error(result::Error);
return result;
}
}
And this mostly works. The problem is that I want to make it so that returning a Promise in the then results in the Promise being evaluated:
class Promise<T> {
Promise<R> Then<R>(Func<T,R> handler){
var result = new Promise<R>();
this.Then((value) => {
result.Complete(handler.Invoke(value));
});
this.Error(result::Error);
return result;
}
Promise<R> Then<R>(Func<T,Promise<R>> handler){
var result = new Promise<R>();
this.Then((value) => {
var nested = handler.Invoke(value);
nested.Then(result::Complete);
nested.Error(result::Error);
});
this.Error(result::Error);
return result;
}
}
The code works, but the problem comes in when trying to invoke it:
var a = new Promise(true);
a.Then((value) => {
return new Promise<bool>(!value);
});
This code errors because the compiler doesnt know which Then function to call. How do you fix this (without explicitly casting)?
I have a function
public Task<IEnumerable<T>> GetVersionsAsync<T>(string key)
{
var request = GetVersionsRequestBuilder.Create(_databaseName, _collectionName).WithKey(key);
return Task.Run(() =>
{
var records = _myReader.GetAllVersionsForKey(request);
var returnValue = records.Select(record =>
{
var document = _mySerializer.Deserialize<T>(record.document);
return document;
});
return returnValue;
});
}
This is skipping first value and giving all rest results
However, if I change the LINQ part to ForEach, then it gives all records
var returnValue = new List<T>();
foreach (var record in records)
{
var document = _mySerializer.Deserialize<T>(record.document);
returnValue.Add(document);
}
return returnValue as IEnumerable<T>;
Is this due to LINQ's Select projection being inside Task.Run?
Do not mix IEnumerables and Tasks. Deferred Execution and parallel execution at the same time are really not playing well together.
I don't know what specifically trigger your weird behavior, but generally speaking, your task is useless, because it does not do what you think it does. It does not deserialize all elements. The task is done almost immediately, and it returns the state-machine that is your IEnumerable. Only when you materialize it, after your task is done, it will actually do what you intend to do with the LinQ.
So, do not mix it. If you want it to run async, do so, but materialize it:
public Task<List<T>> GetVersionsAsync<T>(string key)
{
var request = GetVersionsRequestBuilder.Create(_databaseName, _collectionName).WithKey(key);
return Task.Run(() =>
{
var records = _myReader.GetAllVersionsForKey(request);
var returnValue = records.Select(
record => _mySerializer.Deserialize<T>(record.document)
).ToList(); // materialize it
return returnValue;
});
}
What you propbably should do is make the method async and call the async version of your database reader instead of faking it.
I don't disagree with Nvoigt's advice but I ran two dummy versions of the code. The first version still uses IEnumerable and the second materializes into the List type. I got the same result with each which is that all the results were returned as expected. I have to conclude then that the issue is inside of the Deserialize call.
V1:
static async Task Main()
{
var insideTask = await GetFruitsAsync();
foreach (var obj in insideTask)
{
Console.WriteLine("{0}", obj);
}
return;
}
public static Task<IEnumerable<string>> GetFruitsAsync()
{
IEnumerable<string> fruits = new string[] { "apple", "banana", "mango", "orange",
"passionfruit", "grape" };
return Task.Run(() =>
{
var query = fruits.Select(fruit =>
{
var returnValue = fruit.Substring(0, 2);
return returnValue;
});
return query;
});
}
V2:
static async Task Main()
{
var insideTask = await GetFruitsAsync();
foreach (var obj in insideTask)
{
Console.WriteLine("{0}", obj);
}
return;
}
public static Task<List<string>> GetFruitsAsync()
{
List<string> fruits = new List<string> { "apple", "banana", "mango", "orange",
"passionfruit", "grape" };
return Task.Run(() =>
{
var query = fruits.Select(fruit =>
{
var returnValue = fruit.Substring(0, 2);
return returnValue;
});
return query.ToList();
});
}
The next api returns in Postmen and to the client item1,item2
While I am using ValueTuple to change the names (the names not so important, but I can’t return item1,item2)
public async Task<(List<CategoryFilterResponseDTO> categoryFilters, string MetaDataDescription)> GetCategoryFilterPage([FromBody]categoryFilterRequestDTO categoryFilterRequest)
{
var logItem = new LogDTO();
var result = await _service.GetCategoryFilterPage(categoryFilterRequest);
try
{
OnStart(logItem, new object[] { categoryFilterRequest });
var categoryFilters = result.categoryFilters;
var MetaDataDescription = result.MetaDataDescription;
return (categoryFilters: categoryFilters, MetaDataDescription: MetaDataDescription);
}
}
the method:
public async Task<(List<CategoryFilterResponseDTO> categoryFilters, string MetaDataDescription)> GetCategoryFilterPage(categoryFilterRequestDTO categoryFilterRequestDTO)
{
List<CategoryFilterResponseDTO> categoryFilter = new List<CategoryFilterResponseDTO>();
List<FavoriteDTO> isFavorite = null;
string MetaDataDescription = "";
(List<FilterSortDTO<FlatSearchCategory>>, int) searchCategory = await _clubRepo.CategoryFilterPage(categoryFilterRequestDTO);//BranchesCount
if (searchCategory.Item2 == 0)
{
MetaDataDescription = GetCategoryDetails(categoryFilterRequestDTO.CategoryFirstFatherID.Value).CategoryName;
return (categoryFilters: categoryFilter, MetaDataDescription: MetaDataDescription);
}
You can change your return to the following:
return Ok(new
{
categoryFilters = categoryFilter,
metaDataDescription = MetaDataDescription
});
You will need to change your return type to ActionResult or similar as well.
Because that's the name of the fields on a ValueTuple<,>.
The names given to the values are only available to source code.
I'm new to threading that's why can't understand previous similar questions.
I want to return multiple lists from a JsonResult Method (don't know how can I do it).
I've a list method which will return lists according to the name provided:-
public JsonResult FetchTblData(AdminBundle abundleList)
{
AdminBundle abundle = new AdminBundle();
string innerMesage = string.Empty;
if (Session["AdminBundle"] != null)
abundle = (AdminBundle)Session["AdminBundle"];
//abundle.ListType = Request.Form["TblList"].ToString();
abundle.ListType = abundleList.ListType;
if (abundleList.ListType != string.Empty)
{
using (SMContext db = new SMContext())
{
switch (abundleList.ListType)
{
case "Category":
List<Category> CategoryList = null;
CategoryList = db.CatObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList();
return new JsonResult { Data = CategoryList, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
//Class Starts
case "Board":
List<Board> BoardList = null;
BoardList = db.BoardObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToList();
return new JsonResult { Data = BoardList, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
default:
return new JsonResult { Data = innerMesage, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
//Main default Ends
}
}
}
else
{
return new JsonResult { Data = innerMesage, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
Now I want to call this method from a different method and get returned lists here. But I don't know what and how to do this.
public JsonResult GetMultipleTblResult(AdminBundle aBundleFetch)
{
if (aBundleFetch.TblName.Equals(Constants.Board))
{
aBundleFetch.TblName = Constants.Board;
Thread objBoard = new Thread(FetchTblData(aBundleFetch));
aBundleFetch.TblName = Constants.Class;
Thread objClass = new Thread(FetchTblData(aBundleFetch));
aBundleFetch.TblName = "ClassSubject";
Thread objClassSubject = new Thread(FetchTblData(aBundleFetch));
aBundleFetch.TblName = "MaterialType";
Thread objMatType = new Thread(FetchTblData(aBundleFetch));
objBoard.Start(); objClass.Start(); objClassSubject.Start(); objMatType.Start();
}
return new JsonResult { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
Please feel free to discuss anything. Sorry for my English and Bad coding.
EDIT:
So I have my jsonResult with help of tasks as:-
aBundleFetch.ListType = Constants.Board;
Func<JsonResult> functionBoard = new Func<JsonResult>(() => FetchTblData(aBundleFetch));
JsonResult resBoard = await Task.Factory.StartNew<JsonResult>(functionBoard);
aBundleFetch.ListType = Constants.Class;
Func<JsonResult> functionClass = new Func<JsonResult>(() => FetchTblData(aBundleFetch));
JsonResult resClass = await Task.Factory.StartNew<JsonResult>(functionClass);
aBundleFetch.ListType = Constants.ClassSubject;
Func<JsonResult> functionClassSubject = new Func<JsonResult>(() => FetchTblData(aBundleFetch));
JsonResult resClassSubject = await Task.Factory.StartNew<JsonResult>(functionClassSubject);
aBundleFetch.ListType = Constants.ClassMaterial;
Func<JsonResult> functionClassMaterial = new Func<JsonResult>(() => FetchTblData(aBundleFetch));
JsonResult resClassMaterial = await Task.Factory.StartNew<JsonResult>(functionClassMaterial);
Now I want to Add them into a single JsonResult and pass it to my js file to use it. Any idea how this will be done?
This gives me error:-
JsonResult resultFinal = resBoard.Data + resClass.Data + resClassSubject.Data + resClassMaterial.Data;
In ASP.NET MVC its particularly easy to write asynchronous controller methods
Using async and Task.(Please read the Accepted answer)
Example:
public async Task<JsonResult> FetchTblData(AdminBundle abundleList)
{
AdminBundle abundle = new AdminBundle();
string innerMesage = string.Empty;
if (Session["AdminBundle"] != null)
abundle = (AdminBundle)Session["AdminBundle"];
//abundle.ListType = Request.Form["TblList"].ToString();
abundle.ListType = abundleList.ListType;
if (abundleList.ListType != string.Empty)
{
using (SMContext db = new SMContext())
{
switch (abundleList.ListType)
{
case "Category":
var CategoryList =await db.CatObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToListAsync();
return Json { Data = CategoryList, JsonRequestBehavior.AllowGet };
//Class Starts
case "Board":
var BoardList =await db.BoardObj.Where(x => x.Status_Info == Constants.StatusInfoOne).ToListAsync();
return Json { Data = BoardList, JsonRequestBehavior.AllowGet };
default:
return Json { Data = innerMesage, JsonRequestBehavior.AllowGet };
//Main default Ends
}
}
}
else
{
return Json { Data = innerMesage, JsonRequestBehavior.AllowGet };
}
}