I have an async call that throws an exception if the given record fails processing. That exception is caught as aggregated exception. There is now, a requirement that I have to log a warning message from my async method. I cannot log to TextBox/Grid as it is not allowed to access controls over different threads and I cannot throw an exception as I actually want to log and continue that task. Here is the parent code that launches a task:
private List<OrganizationUser> GenerateDataList(string importFilePath, int lastUserId = -1)
{
var resultList = new List<OrganizationUser>();
// Note: ReadLine is faster than ReadAllLines
var lineCount = File.ReadLines(importFilePath).Count();
LogMessage(new LogEntry(String.Format(" - File has {0} lines.", lineCount)));
var consistancyCounter = 0;
ResetProgress();
using (var fs = File.Open(importFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var bs = new BufferedStream(fs))
{
using (var sr = new StreamReader(bs))
{
string readLine;
while ((readLine = sr.ReadLine()) != null)
{
if (string.IsNullOrEmpty(readLine) || readLine == "---EOF---")
{
break;
}
try
{
var processLineTask = Task.Run(() => GenerateDataListInternal(nextId++, localReadLine, localConsistancyCounter));
processLineTask.Wait();
var result = processLineTask.Result;
if (result != null)
{
resultList.Add(result);
}
}
catch (AggregateException exp)
{
if (exp.InnerExceptions.Count == 1 && exp.InnerExceptions.Any(x => x is DataFileBadColumnNumbers || x is DataFileGenerateListException))
{
LogMessage(new LogEntry(exp.InnerExceptions[0].Message, LogEntryType.Warning));
}
else if (exp.InnerExceptions.Count == 1 && exp.InnerExceptions.Any(x => x is IndexOutOfRangeException))
{
LogMessage(new LogEntry(String.Format(" - Data cannot be parsed at line #{0}. Data is: {1}", localConsistancyCounter + 1, localReadLine), LogEntryType.Warning));
}
else
{
throw;
}
}
}
if (ProgressBarImport.Value <= ProgressBarImport.Maximum)
{
ProgressBarImport.PerformStep();
}
}
}
}
}
In the above code, GenerateDataListInternal
is the method that throws exceptions and now needs to log.
How can I log from within the GenerateDataListInternal
method? I have tried the delegate approach and that simply hangs the application. I have a method that logs to Console, Grid and text file (in that order). Any call to that method from asynced methods fail due to Cross thread operation.
This is already provided through the System.IProgress interface and the Progress class, where T
can be any class, allowing you to report much more than a simple progress percentage.
IProgress<T>.Report
allows you to report progress (or anything else) from inside an asynchronous operation without worrying who will actually handle the report.
Progress<T>
will call an action and/or raise an event on the thread where it was created (eg. the UI thread) whenever your task calls .Report
This example from the .NET Framework Blog displays how easy it is to use IProgress<T>
:
async Task<int> UploadPicturesAsync(List<Image> imageList, IProgress<int> progress)
{
int totalCount = imageList.Count;
int processCount = await Task.Run<int>(() =>
{
int tempCount = 0;
foreach (var image in imageList)
{
//await the processing and uploading logic here
int processed = await UploadAndProcessAsync(image);
if (progress != null)
{
progress.Report((tempCount * 100 / totalCount));
}
tempCount++;
}
return tempCount;
});
return processCount;
}
The async process starts with this code:
private async void Start_Button_Click(object sender, RoutedEventArgs e)
{
//construct Progress<T>, passing ReportProgress as the Action<T>
var progressIndicator = new Progress<int>(ReportProgress);
//call async method
int uploads=await UploadPicturesAsync(GenerateTestImages(), progressIndicator);
}
Note that progressIndicator
is created in the UI thread, so the ReportProgress
method will be called on the UI thread.
UPDATE
From the comments it sounds like you are trying to create your own logging solution and have issues with accessing the log file from multiple threads.
The best solution here would be to use a logging library like log4net
, NLog
or even .NET's Diagnostic classes. All of them work without issues with multiple threads.
IProgress<T>
can still help here, as the delegate that handles a Report event can simply write the message to a log file you already created on the UI thread.
You could write something like this:
var progress = new Progress<LogEntry>(entry =>
{
_logFile.WriteLine("{0} : {1}",entry.EntryType,entry.Message);
});
and report from inside a Task with:
var entry=new LogEntry(...);
progress.Report(entry);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.