How can I make this method asynchronous?
public List<ItemsList> GetAllItems()
{
List<ItemsList> items = new List<ItemsList>();
string query= String.Format("SELECT MFPARTN, MFDSC, sum(QHAVL) as Total FROM IPMFF");
newConnection.OpenConnection();
command = new Command(Query, newConnection.ConnectionInstance);
dataReader = command.ExecuteReader();
ItemsList itemsList = null;
while (dataReader.Read())
{
itemsList = new ItemsList();
if (System.DBNull.Value != dataReader["MFPARTN"])
{
itemsList.ItemNumber = (dataReader["MFPARTN"].ToString());
}
if (System.DBNull.Value != dataReader["MFDSC"])
{
itemsList.ItemDescription = (dataReader["MFDSC"].ToString());
}
if (System.DBNull.Value != dataReader["Total"])
{
itemsList.ItemQuantity = (Convert.ToInt32(dataReader["Total"]));
}
items.Add(itemsList);
}
return items;
}
Merely putting async and await does not seem to be working. Also, how can I make it a Task method? I am new to async methods, so.
Update
I have modified the method like this.
public Task<List<ItemsList>> GetAllItems()
{
List<ItemsList> items = new List<ItemsList>();
string query= String.Format("SELECT MFPARTN, MFDSC, sum(QHAVL) as Total FROM IPMFF");
newConnection.OpenConnection();
command = new Command(Query, newConnection.ConnectionInstance);
dataReader = command.ExecuteReader();
ItemsList itemsList = null;
while (await dataReader.ReadAsync())
{
itemsList = new ItemsList();
if (System.DBNull.Value != dataReader["MFPARTN"])
{
itemsList.ItemNumber = (dataReader["MFPARTN"].ToString());
}
if (System.DBNull.Value != dataReader["MFDSC"])
{
itemsList.ItemDescription = (dataReader["MFDSC"].ToString());
}
if (System.DBNull.Value != dataReader["Total"])
{
itemsList.ItemQuantity = (Convert.ToInt32(dataReader["Total"]));
}
items.Add(itemsList);
}
return items;
}
But it is returning this error when I execute this piece of code.
The model item passed into the dictionary is of type
'System.Threading.Tasks.TaskSystem.Collections.Generic.List, but
this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`
You can define a simple Task<T> to handle your GetAllItems function like so.
public Task<List<ItemsList>> GetListOfItems(){
//Simply pass in your GetAllItems Method
return Task.Factory.StartNew(()=>GetAllItems());
}
You can now create a function to handle the asynchronous operation like under:
public async void GetListOfItemsAsync(){
List<ItemsList> myItems=await GetListOfItems();
//Do something with your list...
}
There are other ways to do this. I would suggest you read up Microsoft's documentation on Asynchronous programming with async and await (C#).
Related
I have a below method which simply unnest the list within list and and create merged record all sub lists
e.g.
testResultDTO similar to
{
dept_name :'',
dept_id:'',
personList :'<List>'
}
personList is list similat to
{
first_name :'',
Last_name:'',
workHistList:'<List>'
}
workHistList is similar to
{
task_name:'',
task_description:''}
`
And the method is below
public async Task<FileDTO> GetWorkList(InputParameter inputParameter)
{
TestResultDTO testResultDTO = await GetPersonWorkList(inputParameter);
var testDto = new List<TestDto>();
if (testResultDTO != null)
{
testDto = personWorkFile(testResultDTO);
}
return testDto;
}
The problem is in method personWorkFile
private List<testDTO> personWorkFile(testResultDTO testResultDTO)
{
List<testDTO> testDTO = new List<testDTO>();
if (testResultDTO.workList is null) return new();
foreach (var testItem in testResultDTO.workList)
{
if (testItem.personList != null && testItem.personList.Any())
{
for (var j = 0; j < testItem.personList.Count; j++)
{
List<testDTO> testDTOTemp = new List<testDTO>();
for (var i = 0; i < testItem.personList[j].workHistList.Count; i++)
{
testDTOTemp.Insert(i, mapper.Map(testItem.personList[j].workHistList[i], mapper.Map<testDTO>(testItem.personList[j])));
}
testDTO.AddRange(testDTOTemp);
}
}
}
return testDTO;
}
`
what it does is it create a finale list with record similar to
{dept_name,dept_id,first_name, last_name, task_name, task_description}
Now when i create a unit test for it, it always fail because, it always insert null in list testDTOTemp.insert
my content of test case code is below
mapperMock.Setup(m => m.Map<List<TestResultDTO>, List<TestDTO>>(testResultDTO)).Returns(testDTO);
//call actual service
var result = await workService.GetWorkList(inputParameter);
Can someone please help me understand why it is not able to insert any data testDTOTemp list
The mock replays the setup by matching the method arguments. In this particular case, it will try to match the method call with the specified instance of TestResultDTO.
Override the Equals() method in TestResultDTO to let the mock compare the argument in order to return as per the setup.
Ex:
public class TestResultDTO
{
public int Id {get; set;}
}
var dto01 = new TestResultDTO(){Id = 1};
var dto02 = new TestResultDTO(){Id = 1};
dto01.Equals(dto02); // This will result in false in spite of having same Ids
If the above class is modified to
public class TestResultDTO
{
public int Id {get; set;}
public override bool Equals(object other)
{
var right = other as TestResultDTO;
if(right == null) return false;
var left = this;
var result = left.Id == right.Id;
return result;
}
}
var dto01 = new TestResultDTO(){Id = 1};
var dto02 = new TestResultDTO(){Id = 1};
dto01.Equals(dto02); // This will result in true
What I need to do
I need to start different instances of a class in a synchronous context using an async method.
Application structure
In my console application I've declared a List<Bot> class:
private List<Bot> _bots = new List<Bot>(new Bot[10]);
the class Bot contains some methods that takes data from internet, so these methods need to be waited. The method structure looks like this:
public class Bot
{
Competition Comp { get; set; }
public async Task StartAsync(int instance)
{
string url = "";
//based on the instance I take the data from different source.
switch(instance)
{
case 0:
url = "www.google.com";
break;
case 1:
url = "www.bing.com";
break;
}
//Comp property contains different groups.
Comp.Groups = await GetCompetitionAsync(Comp, url);
if(Comp.Groups.Count > 0)
{
foreach(var gp in group)
{
//add data inside database.
}
}
}
}
the Competition class have the following design:
public class Competition
{
public string Name { get; set; }
public List<string> Groups { get; set; }
}
I start all the instances of Bot class using the following code:
for(int i = 0; i < _bots.Count - 1; i++)
{
_bots[i].StartAsync(i);
}
this code will call different times StartAsync of Bot class, in this way, I can manage each instance of the bot, and I can eventually stop or start a specific instance in a separate method.
The problem
The method GetCompetitionAsync create a List<string>:
public async Task<List<string>> GetCompetitionAsync(Competition comp, string url)
{
if(comp == null)
comp = new Competition();
List<string> groups = new List<string();
using (var httpResonse = await httpClient.GetAsync(url))
{
string content = await httpResponse.Content.ReadAsStringAsync();
//fill list groups
}
return groups;
}
essentially this method will fill the List<string> available in Comp. Now, if I execute a single instance of StartAsync all works well, but when I run multiple instances (as the for above), the Comp object (which contains the Competition) have all the properties NULL.
So seems that when I have multiple Task running the synchronous context doesn't wait the async context, which in this case fill the List<string>.
When the code reach this line: if(Competition.Groups.Count > 0) I get a NULL exception, because Groups is null, and other Comp properties are NULL.
How can I manage this situation?
UPDATE
After other attempts, I though to create a List<Task> instead of a List<Bot>:
List<Task> tasks = new List<Task>(new Task[10]);
then instead of:
for(int i = 0; i < _bots.Count - 1; i++)
{
_bots[i].StartAsync(i);
}
I did:
for (int i = 0; i < tasks.Count - 1; i++)
{
Console.WriteLine("Starting " + i);
if (tasks[i] == null)
tasks[i] = new Task(async () => await new Bot().StartAsync(i));
apparently all is working well, I got no errors. The problem is: why? I though to something like a deadlock, that I can't even solve using ConfigureAwait(false);.
The last solution also doesn't allow me to access to Bot method because is now a Task.
UPDATE 2
Okay maybe I gotcha the issue. Essentially the await inside the asynchronous method StartAsync is trying to comeback on the main thread, meanwhile the main thread is busy waiting the task to complete, and this will create a deadlock.
That's why moving the StartAsync() inside a List<Task> has worked, 'cause now the async call is now running on a thread pool thread, it doesn't try to comeback to the main thread, and everything seems to works. But I can't use this solution for the reasons explained above.
I'm prefer use Threads instead of Tasks. IMHO, Threads more simple for understanding.
Note: seems that property Bot.Comp in your code is NOT initialized! I fix this issue.
My version of your code:
public class Bot
{
Competition Comp { get; set; }
System.Thread _thread;
private int _instance;
public Bot()
{
Comp = new Competition ();
}
public void Start(int instance)
{
_instance = instance;
_thread = new Thread(StartAsync);
_thread.Start();
}
private void StartAsync()
{
string url = "";
//based on the instance I take the data from different source.
switch(_instance)
{
case 0:
url = "www.google.com";
break;
case 1:
url = "www.bing.com";
break;
}
//Comp property contains different groups.
GetCompetitionAsync(Comp, url);
if(Comp.Groups.Count > 0)
{
foreach(var gp in group)
{
//add data inside database.
}
}
}
public List<string> GetCompetitionAsync(Competition comp, string url)
{
if(comp.groups == null) comp.groups = new List<string>();
using (var httpResonse = httpClient.GetAsync(url))
{
string content = await httpResponse.Content.ReadAsStringAsync();
//fill list groups
}
return groups;
}
}
Then we run threads:
for(int i = 0; i < _bots.Count - 1; i++)
{
_bots[i].Start(i);
}
Each instance of Bot starts method private void StartAsync() in it's own thread.
Note a implementation of method Bot.Start():
public void Start(int instance)
{
_instance = instance;
_thread = new Thread(StartAsync); //At this line: set method Bot.StartAsync as entry point for new thread.
_thread.Start();//At this line: call of _thread.Start() starts new thread and returns **immediately**.
}
This sort of thing is far simpler if you think in terms of lists and "pure" functions-- functions that accept input and return output. Don't pass in something for them to fill or mutate.
For example, this function accepts a string and returns the groups:
List<string> ExtractGroups(string content)
{
var list = new List<string>();
//Populate list
return list;
}
This function accepts a URL and returns its groups.
async Task<List<string>> GetCompetitionAsync(string url)
{
using (var httpResponse = await httpClient.GetAsync(url))
{
string content = await httpResponse.Content.ReadAsStringAsync();
return ExtractGroups(content);
}
}
And this function accepts a list of URLs and returns all of the groups as one list.
async Task<List<string>> GetAllGroups(string[] urls)
{
var tasks = urls.Select( u => GetCompetitionAsync(u) );
await Task.WhenAll(tasks);
return tasks.SelectMany( t => t.Result );
}
You can then stuff the data into the database as you had planned.
var groups = GetAllGroups( new string[] { "www.google.com", "www.bing.com" } );
foreach(var gp in groups)
{
//add data inside database.
}
See how much simpler it is when you break it down this way?
I have a WPF application.
The data is brought from the repository to the ViewModel. What would be the better way to retrieve the data:
Method 1:
In Repository:
public List<LogDetail> GetLogsOfTypeForCase(int caseId, LoggType logType)
{
using (var ctx = new SidraEntitiesNoChangesDetection())
{
var logs = (from l in ctx.Loggs
where l.log_fk_caseid == caseId && l.log_operation == logType.ToString()
select new LogDetail()
{
ColumnName = l.log_columnname,
DateAndTime = l.log_dateandtime,
IdentificationDetail = l.log_identificationDetail,
NewValue = l.log_new_value,
OldValue = l.log_old_value,
TableName = l.log_tablename,
UserCode = l.User.usr_code
}).ToList();
return logs;
}
}
In ViewModel:
await Task.Run(
() =>
{
if (false == this.CancellationTokenSource.IsCancellationRequested)
{
this.CaseLogs = this.dataAdapter.GetLogsOfTypeForCase(this.CaseId, LoggType.S);
}
},
this.CancellationTokenSource.Token
);
or
Method 2
In Repository:
public async Task<List<LogDetail>> GetLogsOfTypeForCase(int caseId, LoggType logType)
{
using (var ctx = new SidraEntitiesNoChangesDetection())
{
var logs = await (from l in ctx.Loggs
where l.log_fk_caseid == caseId && l.log_operation == logType.ToString()
select new LogDetail()
{
ColumnName = l.log_columnname,
DateAndTime = l.log_dateandtime,
IdentificationDetail = l.log_identificationDetail,
NewValue = l.log_new_value,
OldValue = l.log_old_value,
TableName = l.log_tablename,
UserCode = l.User.usr_code
}).ToListAsync();
return logs;
}
}
and in ViewModel
protected override async void Load()
{
if (false == this.CancellationTokenSource.IsCancellationRequested)
{
this.CaseLogs = await this.dataAdapter.GetLogsOfTypeForCase(this.CaseId, LoggType.S);
}
}
From what I have read, Method 1 would be preferred, but what be the advantages?
Method 2 is preferable, because it uses one less thread.
It also can (with some modifications) properly support cancellation:
public async Task<List<LogDetail>> GetLogsOfTypeForCase(int caseId, LoggType logType, CancellationToken token)
{
...
}).ToListAsync(token);
...
}
protected override async void Load()
{
this.CaseLogs = await this.dataAdapter.GetLogsOfTypeForCase(this.CaseId, LoggType.S, this.CancellationTokenSource.Token);
}
Method 2. The Async versions of these methods do all the "heavy lifting" for you, that was what they were designed for. Task.Run() is a heavy process call, you are required to handle all the potential errors and failures yourself, you don't need a sledge hammer here, just a light weight finishing hammer.
By this I mean that you are trying to create what the framework has already done for you, this is where Async calls were meant to be used so why not just use them?
Using the IMobileServiceTable interface, how can I call result of a method synchronously. E.g. GetTask method below :
public class ItemsManager {
IMobileServiceTable<Item> itemTable;
private ItemViewModel _itemViewModel = new ItemViewModel ();
public ItemsManager()
{
this.itemTable = App.client.GetTable<Item>();
App.SetItemsManager(this);
}
//This seems to just hang
public Item GetTask(string id, string barcode)
{
var qry = itemTable.Where(a => a.ItemID == id || a.BarCodeID == id);
return qry.Query.FirstOrDefault();
}
}
I'm trying to get the result via scan method, and extract the item record to another form :
async void OnProductScanClicked (object sender, EventArgs e)
{
var result = await BarCodes.Instance.Read ();
if (!result.Success) {
await this.DisplayAlert ("Scan Failed", "Failed to get barcode", "OK");
} else {
var msg = String.Format ("Barcode : {0} - {1}", result.Format, result.Code);
App.SetItemsManager(new ItemsManager());
var item = App.ItemManager.GetTask(result.Code, result.Format.ToString());
if (item == null) {
await this.DisplayAlert ("Item Not Found!", msg, "OK");
}
else {
var myPage = new ItemsXaml();
myPage.BindingContext = item;
await Navigation.PushModalAsync (myPage);
}
}
}
I am just new to this stuff, and now have figure out on how to this. We just have to add the await method when calling any asynchronous function.
hi what is the easiest way to implement asynch operations on WPF and MVVM, lets say if user if user hits enter when on a field i want to launch a command and then return back while a thread will do some search operations and then come back and update the properties so notification can update the bindings.
thanks!
Rob Eisenberg showed a really clean implementation of running async operations in MVVM during his MIX10 talk. He has posted the source code on his blog.
The basic idea is that you implement the command as returning an IEnumerable and use the yield keyword to return the results. Here is a snippet of code from his talk, which does a search as a background task:
public IEnumerable<IResult> ExecuteSearch()
{
var search = new SearchGames
{
SearchText = SearchText
}.AsResult();
yield return Show.Busy();
yield return search;
var resultCount = search.Response.Count();
if (resultCount == 0)
SearchResults = _noResults.WithTitle(SearchText);
else if (resultCount == 1 && search.Response.First().Title == SearchText)
{
var getGame = new GetGame
{
Id = search.Response.First().Id
}.AsResult();
yield return getGame;
yield return Show.Screen<ExploreGameViewModel>()
.Configured(x => x.WithGame(getGame.Response));
}
else SearchResults = _results.With(search.Response);
yield return Show.NotBusy();
}
Hope that helps.
How about a BackgroundWorker instance to call your command on the VM ?
Update:
Scratch the above suggestion.. There's an online video on MVVM by Jason Dolinger.. I recommend you take a look at that. It's a cleaner way where the view is thin/ does not hold any threading code.
To summarize:
the VM ctor caches the Dispatcher.CurrentDispatcher object (main thread).
when updating the backing store (results), use
_dispatcher.BeginInvoke( () => _results.AddRange( entries) ) so that the UI is updated correctly.
In Shawn Wildermuth's MSDN Article he did something like this:
check out the article here:
http://msdn.microsoft.com/en-us/magazine/dd458800.aspx
and his more recent blog post here:
http://wildermuth.com/2009/12/15/Architecting_Silverlight_4_with_RIA_Services_MEF_and_MVVM_-_Part_1
public interface IGameCatalog
{
void GetGames();
void GetGamesByGenre(string genre);
void SaveChanges();
event EventHandler<GameLoadingEventArgs> GameLoadingComplete;
event EventHandler<GameCatalogErrorEventArgs> GameLoadingError;
event EventHandler GameSavingComplete;
event EventHandler<GameCatalogErrorEventArgs> GameSavingError;
}
with an implementation like this:
public class GameCatalog : IGameCatalog
{
Uri theServiceRoot;
GamesEntities theEntities;
const int MAX_RESULTS = 50;
public GameCatalog() : this(new Uri("/Games.svc", UriKind.Relative))
{
}
public GameCatalog(Uri serviceRoot)
{
theServiceRoot = serviceRoot;
}
public event EventHandler<GameLoadingEventArgs> GameLoadingComplete;
public event EventHandler<GameCatalogErrorEventArgs> GameLoadingError;
public event EventHandler GameSavingComplete;
public event EventHandler<GameCatalogErrorEventArgs> GameSavingError;
public void GetGames()
{
// Get all the games ordered by release date
var qry = (from g in Entities.Games
orderby g.ReleaseDate descending
select g).Take(MAX_RESULTS) as DataServiceQuery<Game>;
ExecuteGameQuery(qry);
}
public void GetGamesByGenre(string genre)
{
// Get all the games ordered by release date
var qry = (from g in Entities.Games
where g.Genre.ToLower() == genre.ToLower()
orderby g.ReleaseDate
select g).Take(MAX_RESULTS) as DataServiceQuery<Game>;
ExecuteGameQuery(qry);
}
public void SaveChanges()
{
// Save Not Yet Implemented
throw new NotImplementedException();
}
// Call the query asynchronously and add the results to the collection
void ExecuteGameQuery(DataServiceQuery<Game> qry)
{
// Execute the query
qry.BeginExecute(new AsyncCallback(a =>
{
try
{
IEnumerable<Game> results = qry.EndExecute(a);
if (GameLoadingComplete != null)
{
GameLoadingComplete(this, new GameLoadingEventArgs(results));
}
}
catch (Exception ex)
{
if (GameLoadingError != null)
{
GameLoadingError(this, new GameCatalogErrorEventArgs(ex));
}
}
}), null);
}
GamesEntities Entities
{
get
{
if (theEntities == null)
{
theEntities = new GamesEntities(theServiceRoot);
}
return theEntities;
}
}
}