.ToListAsync() vs .ToList() + Task.Run - c#

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?

Related

Executing same function from several async methods simultenously causes errors

Here is a piece of code, where I try to execute different async methods, that need to be executed in specific order (the await, and Task.WhenAll() parts).
//Some other tasks before
Task<bool> taskIfcQuantityArea = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcQuantityArea");
});
Task<bool> taskIfcQuantityLength = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcQuantityLength");
});
Task<bool> taskIfcSiUnit = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcSiUnit");
});
Task<bool> taskIfcPropertySingleValue = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcPropertySingleValue");
});
//uses IfcPerson, IfcOrganization
Task<bool> taskIfcPersonAndOrganization = Task.Run<bool>(() =>
{
return addGroupStringToDictionary("IfcPersonAndOrganization");
});
//uses IfcOrganization
Task<bool> taskIfcApplication = Task.Run(async () =>
{
await taskIfcSiUnit;
return addGroupStringToDictionary("IfcApplication");
});
//uses IfcSiUnit
Task<bool> taskIfcMeasureWithUnit = Task.Run(async () =>
{
await taskIfcSiUnit;
return addGroupStringToDictionary("IfcMeasureWithUnit");
});
//some other tasks after.
When I do that job synchronously, all works fine, but when I do it in async, I have some random errors. At every test, the errors come randomly.
The only thing I see that could go wrong, is they all execute the same function addGroupStringToDictionary.
Here is the function :
private bool addGroupStringToDictionary(string typeName)
{
//int processCount = await Task.Run<int>(() =>
//{
GroupedListStrings groupElt = this.listGrouppedStrings.FirstOrDefault(x => x.Type == typeName.ToUpper());
if (groupElt != null)
{
List<string> listStringInGroup = groupElt.ListStrings;
foreach (string line in listStringInGroup)
{
try
{
if(typeName== "IfcLocalPlacement($")
{
typeName = "IfcLocalPlacement";
}
var type = Type.GetType("Ifc."+typeName);
if (typeName == "IfcPropertySingleValue" || typeName == "IfcDirection" || typeName == "IfcSiUnit" || typeName == "IfcQuantityLength" || typeName == "IfcQuantityArea" || typeName == "IfcQuantityVolume" || typeName == "IfcQuantityWeight")
{
try
{
object instance = Activator.CreateInstance(type, line);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
else if (typeName == "IfcOpeningElement")
{
try
{
object instance = Activator.CreateInstance(type, line, this.listDictionaries, this.DictionaryBolts);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
else
{
try
{
object instance = Activator.CreateInstance(type, line, this.listDictionaries);
this.addToListDictionary((IfcElement)instance);
}
catch
{
}
}
}
catch
{
this.addError(line);
}
}
this.listGrouppedStrings.Remove(groupElt);
this.reportProgressImport();
}
//return 100;
//});
this.reportProgressImport();
return true;
}
The catch got 1-2 times over a bit more than 1 million lines.
At each test the errors come randomly.
Is it possible that running the function simultaneously from several async methods, this is what causes the problem?
Here is the addToListDictionary function :
private void addToListDictionary(IfcElement elt)
{
if(elt.ErrorFound)
{
this.listReadButError.Add(elt);
return;
}
string type = elt.GetType().ToString();
if (elt is IfcRepere)
{
type = "Ifc.IfcRepere";
}
else if (elt is IfcRepereType)
{
type = "Ifc.IfcRepereType";
}
else if (elt is IfcPhysicalSimpleQuantity)
{
type = "Ifc.IfcPhysicalSimpleQuantity";
}
else if (elt is IfcProfileDef)
{
type = "Ifc.IfcProfileDef";
}
else if (elt is IfcGeometricRepresentationContext)
{
type = "Ifc.IfcGeometricRepresentationContext";
}
GroupDictionary group = this.ListDictionaries.FirstOrDefault(x => x.Name == type);
if(group==null)
{
group = new GroupDictionary { Name = type };
this.ListDictionaries.Add(group);
}
group.ListElements[elt.ID] = elt;
if (elt is IfcMechanicalFastener)
{
IfcMechanicalFastener bolt = (IfcMechanicalFastener)elt;
this.DictionaryBolts[bolt.Tag] = bolt;
}
else if(elt is IfcProject)
{
this.listProjects.Add((IfcProject)elt);
}
else if(elt is IfcElementAssembly ifcAss)
{
this.DictionaryIfcElementAssemblies[ifcAss.Key] = ifcAss;
}
}
Also some additive information about my ListDictionaries :
private List<GroupDictionary> listDictionaries = new List<GroupDictionary>();
public List<GroupDictionary> ListDictionaries { get { return this.listDictionaries; } set { this.listDictionaries = value; } }
And the class GroupDictionary
public class GroupDictionary
{
string name { get; set; }
public string Name { get { return this.name; } set { this.name = value; } }
public ConcurrentDictionary<int, IfcElement> ListElements = new ConcurrentDictionary<int, IfcElement>();
public GroupDictionary()
{
}
}
I made different GroupDictionary because as soon as I don't need one of them, I delete it to free space.
I have one dictionary with IfcPoint, I need it to gt IfcPolyLine (lines), but when I finish to treat all objects using IfcPoint, I clear remove the corresponding GroupDictionary in order to free some memory.
You have a obvious thread-safety issues here (i.e. you are trying to perform some operation which is not thread safe from multiple threads at a time). There are multiple ways you can try tackling it - using locks, or some synchronization primitives.
But in this case it seems that major source of issues is working with standard collections from multiple threads, which is not thread-safe (because thread-safety usually comes with performance price and is not always needed). You can start from switching to appropriate collections from System.Collections.Concurrent namespace.
To go down deeper I recommend free e-book by Joseph Albahari Threading in C#.

Manage boolean condition with Include in EF Core

I know is a simple problem, but I don't find the way to manage this properly. I think it's ugly, and I'm sure a better way exists.
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
if (!includeDocumentTypes && !includeDocumentDescriptions)
{
return await _context.DocumentCategories.ToListAsync();
}
else if (includeDocumentTypes && !includeDocumentDescriptions)
{
return await _context.DocumentCategories.Include(dc => dc.DocumentTypes).ToListAsync();
}
else
{
return await _context.DocumentCategories.Include(dc => dc.DocumentTypes).ThenInclude(dt => dt.DocumentDescriptions).ToListAsync();
}
}
The goal is to include nested datas from boolean flags.
More information about my class below :
public class DocumentCategoriesRepository : GenericRepository<DocumentCategory>, IDocumentCategoriesRepository
{
public DocumentCategoriesRepository(myDbContext context) : base(context)
{
}
// Here is my method
}
What about
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
var categories = _context.DocumentCategories.AsQueryable();
if (includeDocumentTypes)
{
categories = categories.Include(dc => dc.DocumentTypes);
}
if (includeDocumentDescriptions)
{
categories = categories
.Include(dc => dc.DocumentTypes)
.ThenInclude(dt => dt.DocumentDescriptions);
}
return await categories.ToListAsync();
}
This should work as including DocumentTypes multiple times is not a problem for EF and is handled as if it was included just once.
How about something like this, where you build up the query in distinct steps.
CAVEAT - This code hasn't been tested and may not work out of the box. The type of query would likely need some work
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
var query = _context.DocumentCategories;
if (includeDocumentTypes)
{
query = query.Include(dc => dc.DocumentTypes);
if (includeDocumentDescriptions)
{
query = query.ThenInclude(dt => dt.DocumentDescriptions);
}
}
return await query.ToListAsync()
}
You can make simple edits like below
public async Task<List<DocumentCategory>> GetAll(bool includeDocumentTypes = false, bool includeDocumentDescriptions = false)
{
var lstCategoires = _context.DocumentCategories;
if (includeDocumentTypes)
{
lstCategoires = includeDocumentDescriptions ? lstCategoires.Include(dc => dc.DocumentTypes).ThenInclude(dt => dt.DocumentDescriptions) :
lstCategoires = lstCategoires.Include(dc => dc.DocumentTypes);
}
return await lstCategoires.ToListAsync();
}

Replace sync method with async one

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#).

Thread affinity with ReactiveUI 6

The scenario (WPF desktop app, .NET 4.6):
I have a listbox which display some "tasks".
The goal is to start an asynchronous process that will iterate through all tasks, executing each one of them.
It is a long running process so the desired behavior is to disable most of the commands without locking the UI so the user can still cancel it.
It should flag each tasks as it goes (stand by, running, complete) so the UI can be dynamically updated providing feedback to end user (using styles based on the "Status" enum).
The Problem
When the command (ExecuteTasks) is executed I get the message:
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
My question is: how can I get around this problem using ReactiveUI? I believe the answer is somewhere around schedulers but I couldn't figure it out so far.
Here is what the code looks like:
public ReactiveCommand<object> AddTask { get; }
public ReactiveCommand<object> ExecuteTasks { get; }
public IPlugin SelectedTask
{
get { return selectedTask; }
set { this.RaiseAndSetIfChanged(ref selectedTask, value); }
}
public ReactiveList<IPlugin> Tasks
{
get { return tasks; }
}
public ReactiveList<PluginFactoryGroup> TaskFactories
{
get
{
return taskFactories;
}
}
public AppViewModel(IExecutionContext context, IEnumerable<PluginFactoryGroup> taskFactories)
{
this.context = context;
// initialize lists
tasks = new ReactiveList<IPlugin>() { ChangeTrackingEnabled = true };
this.taskFactories = new ReactiveList<PluginFactoryGroup>(taskFactories);
// create observables to determine whether or not commands can be executed
var canEdit = /*...*/
var canExecute = /*...*/
// initialize commands
AddTask = ReactiveCommand.Create(canEdit);
AddTask.Subscribe(_ => {
if (SelectedFactory != null)
{
var t = SelectedFactory.Create(this.context);
Tasks.Add(t);
SelectedTask = t;
}
});
ExecuteTasks = ReactiveCommand.CreateAsyncTask(canExecute, _ =>
{
return Task.Run(() =>
{
object result = null;
foreach (var item in Tasks)
{
item.Clear();
item.Validate();
}
if (Tasks.Any(e => e.Status == TaskStatus.Error))
{
Tasks.Reset();
return result;
}
foreach (var item in Tasks)
{
item.Status = XrmTools.Plugins.TaskStatus.Running;
item.Execute();
item.Status = XrmTools.Plugins.TaskStatus.Completed;
}
return result;
});
});
}
All UI updates (ReactiveList add/remove, or IPlugin property change) need to happen in UI thread. In your case, assuming item.Execute() is the lengthy operation you want to happen in the background, you should use async/await instead of Task.Run, e.g: your code should look like:
ExecuteTasks = ReactiveCommand.CreateAsyncTask(canExecute, async _ =>
{
object result = null;
foreach (var item in Tasks)
{
item.Clear();
item.Validate();
}
if (Tasks.Any(e => e.Status == TaskStatus.Error))
{
Tasks.Reset();
return result;
}
foreach (var item in Tasks)
{
item.Status = XrmTools.Plugins.TaskStatus.Running;
await ExecuteAsync(item);
item.Status = XrmTools.Plugins.TaskStatus.Completed;
}
return result;
});
Task ExecuteAsync(IPlugin item)
{
return Task.Run(() => item.Execute());
}
Have a look at this reference if you need more inspiration.

how do you implement Async Operations in C# and MVVM?

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;
}
}
}

Categories