- Details
- Written by: Stanko Milosev
- Category: C#
- Hits: 232
A task returned by Task.Run() really is saying "I want you to execute this code separately"; the exact thread on which that code executes depends on a number of factors.So, here is first my method with async. First install Microsoft.Data.SqlClient here notice that old System.Data.SqlClient is deprecated. Now the method:
return Task.Run(() => ThreadReadFromDatabaseAsync(connectionString, sql)); private async Task ThreadReadFromDatabaseAsync(string connectionString, string sql) { DateTime start = DateTime.Now; try { await using SqlConnection connection = new SqlConnection(connectionString); await connection.OpenAsync(); await using SqlCommand command = new SqlCommand(sql, connection); command.CommandTimeout = 3600; await using SqlDataReader reader = await command.ExecuteReaderAsync(); int recdCnt = 0; while (reader.Read()) { UpdateUi.Execute($"{start} Reading using tasks record {recdCnt++}", LblRecordCount, Form); object[] values = new object[reader.FieldCount]; reader.GetValues(values); } } catch (Exception ex) { UpdateUi.Execute($"Error in task execution: {ex.Message}", LblError, Form); } finally { UpdateUi.Execute($"{DateTime.Now} Done with task execution", LblStatus, Form); } }Here notice line:
while (reader.Read())That I am not using ReadAsync. The problem is that at ReadAsync will stuck on reading from DB after few hundert records, this looks like bug described here. Now another example with thread:
var thread = new Thread(() => ThreadReadFromDatabase(connectionString, sql)) { Name = "ThreadReadFromDatabase", IsBackground = true }; thread.Start(); private void ThreadReadFromDatabase(string connectionString, string sql) { DateTime start = DateTime.Now; try { using SqlConnection connection = new SqlConnection(connectionString); connection.Open(); using SqlCommand command = new SqlCommand(sql, connection); command.CommandTimeout = 3600; using SqlDataReader reader = command.ExecuteReader(); int recdCnt = 0; while (reader.Read()) { UpdateUi.Execute($"{start} Reading using threads record {recdCnt++}", LblRecordCount, Form); object[] values = new object[reader.FieldCount]; reader.GetValues(values); } } catch (Exception ex) { UpdateUi.Execute($"Error in thread execution: {ex.Message}", LblError, Form); } finally { UpdateUi.Execute($"{DateTime.Now} Done with thread execution", LblStatus, Form); } }Full example download from here UPDATE 2025-03-12: Avoid using Task.Run for long-running work that blocks the thread Also from the book Concurrency in C# Cookbook by Stephen Cleary: As soon as you type new Thread(), it’s over; your project already has legacy code Also check this Stack Overflow answer.
- Details
- Written by: Stanko Milosev
- Category: C#
- Hits: 331
namespace LoggerWrapper.Logger; public class TextBoxLogger : ILogger { public TextBox? TextBox { get; set; } public void Log(LogEntry entry) { TextBox?.AppendText( $@"[{entry.Severity}] {DateTime.Now} {entry.Message} {entry.Exception}"); TextBox?.AppendText(Environment.NewLine); } }Notice:
public TextBox? TextBox { get; set; }Later, I will connect this property to the actual TextBox in Form1. Here’s what my Program.cs looks like:
using LoggerWrapper.Logger; namespace LoggerWrapper { internal static class Program { [STAThread] static void Main() { ApplicationConfiguration.Initialize(); var textBoxLogger = new TextBoxLogger(); var someDiClassHandler = new SomeDiClassHandler(textBoxLogger); Form1 form1 = new Form1(someDiClassHandler); textBoxLogger.TextBox = form1.TbLogger; Application.Run(form1); } } }Notice:
textBoxLogger.TextBox = form1.TbLogger;In Form1, I introduced a property named TbLogger:
namespace LoggerWrapper; public partial class Form1 : Form { public TextBox? TbLogger { get; } private readonly SomeDiClassHandler _someDiClassHandler; public Form1(SomeDiClassHandler someDiClassHandler) { InitializeComponent(); TbLogger = tbLogger; _someDiClassHandler = someDiClassHandler; } private void btnStart_Click(object sender, EventArgs e) { SomeDiClassCommand someDiClassCommand = new SomeDiClassCommand(); _someDiClassHandler.Execute(someDiClassCommand); } }After creating Form1 I will assign TextBox to my TextBoxLogger, and in Form1 constructor I will assign TbLogger to real TextBox, in my case tbLogger:
TbLogger = tbLogger;Example download from here
- Details
- Written by: Stanko Milosev
- Category: C#
- Hits: 453
<ItemGroup> <PackageReference Include="IKVM.Maven.Sdk" Version="1.8.2"/> <MavenReference Include="net.sf.saxon:Saxon-HE" version="12.5"/> <PackageReference Include="SaxonHE12s9apiExtensions" Version="12.5.9.4"/> </ItemGroup>Use this method to return result as a string which later you can convert to XML as I already explained here:
using net.sf.saxon.s9api; using net.liberty_development.SaxonHE12s9apiExtensions; ... string ValidateXmlFile(string xmlFile, string xslFile) { Processor processor = new Processor(); DocumentBuilder builder = processor.newDocumentBuilder(); XsltTransformer xsltTransformer = processor.newXsltCompiler().Compile(new Uri(xslFile)).load(); var inputNode = builder.Build(new Uri(xmlFile)); xsltTransformer.setInitialContextNode(inputNode); using var stringWriter = new StringWriter(); Serializer serializer = processor.NewSerializer(stringWriter); xsltTransformer.setDestination(serializer); xsltTransformer.transform(); return stringWriter.ToString(); }Example download from here.