Background thread doing DB access hangs UI - c#

I've made a small utility program to test if a PC has a possibility to connect to a certain Oracle database.
To keep the UI responsive, and see the progress steps, I put the DB code in a background thread. To my amazement, the UI still hangs (but not as much).
It's really no big deal with this app, but I thought that the case is interesting in general, that the DB code in the thread hangs the UI thread!
private void bgwDataAccess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgw = sender as BackgroundWorker;
try
{
bgw.ReportProgress(0, "Starting...\r\n");
bgw.ReportProgress(0, "Active ConnectionString:\r\n");
bgw.ReportProgress(0, Settings.Default.ConnctionString + "\r\n\r\n");
OracleConnection con = new OracleConnection(Settings.Default.ConnctionString);
OracleCommand cmd = new OracleCommand("SELECT Count(*) FROM MYTABLE", con);
bgw.ReportProgress(0, "Opening db...\r\n");
con.Open();
bgw.ReportProgress(0, "Opened.\r\n\r\n");
bgw.ReportProgress(0, "Executing SQL-query...\r\n");
Object result = cmd.ExecuteScalar();
bgw.ReportProgress(0, String.Format("Result: {0}\r\n\r\n", result.ToString()));
con.Close();
}
catch (Exception)
{
throw;
}
}
private void bgwDataAccess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
txtResult.Text += e.UserState;
}

Do you access the Oracle namespace anywhere in your code before this? This is just a guess, but maybe that pause is your application loading the required .dlls
You could try pre-loading the module. I use something like the code below in my applications. First I display a splash screen to show the app is loading and then call the snippet below to load all of the required dlls. That way, once the app is loaded, there's no pauses further down the line.
void PreloadDLLs()
{
Assembly^ assembly = Assembly::GetEntryAssembly();
array<System::Reflection::AssemblyName^>^ referencedAssemblies = assembly->GetReferencedAssemblies();
for each(System::Reflection::AssemblyName^ referencedAssemblyName in referencedAssemblies)
{
try
{
Assembly^ a = assembly->Load(referencedAssemblyName);
}
catch(System::Exception^ /*e*/)
{
}
}
}
Apologies for the C++/CLI syntax, but hopefully you can see how to convert that to C# - mine's a little rusty :-)
[Edit] I think this is pretty much C#:
using System;
using System.Reflection;
private void PreloadDLLs()
{
Assembly assembly = Assembly.GetEntryAssembly();
System.Reflection.AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies();
foreach(System.Reflection.AssemblyName referencedAssemblyName in referencedAssemblies)
{
try
{
Assembly a = assembly.Load(referencedAssemblyName);
}
catch
{
}
}
}

You can change query to "SELECT top 1 id FROM MYTABLE" and impact will be the same.
In case impact is not caused by those operations, you can use profiler to figure out which .net code causes impact.

Related

MSDTC transaction being promoted to distributed on query executed outside of transaction scope

I have a SQL Server 2008 database with two tables, a .NET 4.5 application using a TransactionScope with two queries to the database, each using their own separate connection which shares a single connection string.
This code that works fine:
internal static void Main(string[] args)
{
string connStr = string.Format(
"Data Source={0};Initial Catalog={1};" +
"Integrated Security=True", "My DB Server", "My Database");
using (var scope = new TransactionScope())
{
string query1 = "(actual query not relevant)";
var ds = GetSqlServerDataSet(query1, connStr);
string query2 = "(actual query not relevant)";
var ds1 = GetSqlServerDataSet(query2, connStr);
scope.Complete();
}
}
private static System.Data.DataSet GetSqlServerDataSet(string usingQuery,
string usingConnectionString,
params System.Data.SqlClient.SqlParameter[] withParameters)
{
using (var ds = new System.Data.DataSet())
using (var conn = new System.Data.SqlClient.SqlConnection(usingConnectionString))
using (var command = new System.Data.SqlClient.SqlCommand(usingQuery, conn))
{
command.Parameters.AddRange(withParameters);
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
{
adapter.Fill(ds);
}
return ds;
}
}
Now, if I throw an exception inside the scope to test roll-back, that's when stuff gets strange. I'm using a referenced class library I've written to persist info about exceptions to a database. It's the same type of setup -- same SQL Server, .NET version, identical ADO.NET code, etc. It's just writing to a different database.
Here's how it works: I've added this method to my app:
private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e)
{
ExceptionHandling.Core.Main.ProcessException((Exception) e.ExceptionObject);
Environment.Exit(0);
}
and I've added this line to the top of my Main method:
AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;
Now when I throw an exception, e.g. throw new Exception("blah"); at the bottom of Main right before scope.Complete(), it automatically jumps to HandleUnhandledException, and the rest happens as I've described above. This results in System.Transactions.TransactionManagerCommunicationException with message:
Network access for Distributed Transaction Manager (MSDTC) has been
disabled. Please enable DTC for network access in the security
configuration for MSDTC using the Component Services Administrative
tool.
This happens on the call to Connection.Open() in my class library.
So what's happening here? I know what the error is telling me, and I know how to fix it. My question is why is this happening in the first place? I thought the using statement would take care of rolling back the transaction before it hits my HandleUnhandledException method, so there shouldn't be two transactions involved here. Or am I wrong?
It happens because when your HandleUnhandledException method runs - you are still inside using block and it's finally part has not been run yet. It's easy to verify with this code:
class Program {
static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
try {
throw new Exception("test");
}
finally {
Console.WriteLine("finally");
}
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
Console.WriteLine("handle exception");
}
}
Which outputs:
handle exception
Unhandled exception: System.Exception: test ...
finally
You may want to read this question (and comments, from Eric Lipper for example) for discussion of related behavior. But I think reasons are not very relevant to your question - we can say it just works like this.
Given that information it's clear why you observe your exception - you are still inside uncommited transaction scope and trying to open connection to different database.
One solution that comes to mind in your case is check if we are inside transaction and suppress it if yes (because you anyway don't want your logging code to affect ambient transaction in any way):
private static void HandleException(Exception ex) {
TransactionScope scope = null;
if (Transaction.Current != null)
scope = new TransactionScope(TransactionScopeOption.Suppress);
try {
// do stuff
}
finally {
scope?.Dispose();
}
}
Or maybe just always run inside supressed TransactionScope.

Thread.Abort doesn't release a file

I made a code that create a Database in .sqlite, all working good but I want to be sure that when the user start for the first time the application the Database population must be completed. If the user abort the database population, the database must be deleted (because the application don't working with an incomplete resource). Now I've used the thread for execute the method that create this Database, and I've declared the thread variable global in the class, like:
Thread t = new Thread(() => Database.createDB());
The Database.createDB() method create the DB. All working perfect, the DB is created correctly. Now I fire the closing of the window that creating the DB like:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
MessageBoxResult result = MessageBox.Show(
#"Sure?",
"Attention", MessageBoxButton.YesNo, MessageBoxImage.Question);
try
{
if (result == MessageBoxResult.Yes)
{
t.Abort();
if (File.Exists("Database.sqlite"))
{
File.Delete("SoccerForecast.sqlite");
Process.GetCurrentProcess().Kill();
} ....
The event was fired correct and the thread stopped, but when the condition start if (File.Exists("Database.sqlite")) the compiler tell me:
Can't delete file - in using by another process.
But I've stopped the thread, why this exception appear? What I doing wrong?
UPDATE:
In CreateDb() method I also have a call to other method of different class, one of this have the structure like this:
public void setSoccer()
{
Database.m_dbConnection.Open();
string requestUrl = "...";
string responseText = Parser.Request(requestUrl);
List<SoccerSeason.RootObject> obj = JsonConvert.DeserializeObject<List<SoccerSeason.RootObject>>(responseText);
foreach (var championships in obj)
{
string sql = "string content";
SQLiteCommand command = new SQLiteCommand(sql, Database.m_dbConnection);
try
{
command.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
string query = "select * from SoccerSeason";
SQLiteCommand input = new SQLiteCommand(query, Database.m_dbConnection);
SQLiteDataReader reader = input.ExecuteReader();
int i = 0;
while (reader.Read())
{
//reading data previously inserted in the database
}
Database.m_dbConnection.Close(); /
}
I was wondering where I should put the flag variable because this code have a different loop inside.
It could be that when you're aborting the thread it's not cleanly closing the database connections, hence the error you're seeing.
Might I suggest a slight redesign because using Thread.Abort is not ideal.
Instead use a variable as a cancel flag to notify the thread to shut down.
Then when the thread detects that this cancel flag is set it can properly close connections and handle the database delete itself.
Update:
A brief example to illustrate what I mean; it ain't pretty and it won't compile but it gives the general idea.
public class Database
{
public volatile bool Stop= false;
public void CreateDb()
{
if(!Stop)
{
// Create database
}
if(!Stop)
{
// Open database
// Do stuff with database
}
// blah blah ...
if(Stop)
{
// Close your connections
// Delete your database
}
}
}
...
protected override void OnClosing(CancelEventArgs e)
{
Database.Stop = true;
}
And now that you know roughly what you're looking for I heartily recommend Googling for posts on thread cancellation by people who know what they're talking about that can tell you how to do it right.
These might be reasonable starting points:
How to: Create and Terminate Threads
.NET 4.0+ actually has a CancellationToken object with this very purpose in mind Cancellation in Managed Threads

Response never ends with PageAsyncTask?

I have to support an old project which uses PageAsyncTask with webforms vs2010 fw4.
However this simple code does compile + execute( no errors in debug mode / trace mode) but the response never ends.
Looking in debug mode + breakpoints - it reaches all stages of code.
public partial class _Default2 : System.Web.UI.Page
{
IAsyncResult BeginGetData(object sender, EventArgs e, AsyncCallback callback, object state)
{
SqlConnection con = new SqlConnection(#"Data Source=... Asynchronous Processing=True;");
var sql = #" SELECT [NAME] FROM [WebERP].[dbo].[t]";
{
SqlCommand _cmd = null;
try
{
_cmd = new SqlCommand(sql, con);
_cmd.CommandTimeout = 100000;
con.Open();
return _cmd.BeginExecuteReader(EndGetData, _cmd);
}
catch (Exception ex)
{
if (_cmd != null) _cmd.Dispose();
con.Close();
throw;
}
}
}
void EndGetData(IAsyncResult ar)
{
(ar.AsyncState as SqlCommand).EndExecuteReader(ar);
Response.Write(1111); // HttpContext.Current.Response also doesnt help
}
void TimeoutData(IAsyncResult ar)
{
Response.Write("Could not retrieve data!");
}
protected void Page_Load(object sender, EventArgs e)
{
PageAsyncTask task = new PageAsyncTask(BeginGetData, EndGetData, TimeoutData, null, true);
Page.RegisterAsyncTask(task);
Page.ExecuteRegisteredAsyncTasks();
}
}
Question
The response never ends. All I see is :
What am I missing ?
(nb Async="true" is on the page directive , also - code was simplified just to describe the case )
I think the problem here is that you pass the same BeginGetData and EndGetData callbacks to both BeginExecuteReader and new PageAsyncTask(). They should be different: what you pass to PageAsyncTask is "outer" to what you pass to BeginExecuteReader.
You'd call BeginExecuteReader from the begin callback you'd pass to PageAsyncTask, and you'd be actually required to call the AsyncCallback callback provided to you there by ASP.NET (you'd call it when the async operation would have finished, i.e., from your EndGetData).
It would be so much easier if you could use the PageAsyncTask(Func<Task>) override, but I don't think you can target .NET 4.5 with VS2010.

WMI call works in GUI thread (STA Thread), why does it fail in background threads (MTA Threads)?

Okay, I have a method which simply backs up the Windows event logs as *.evt files.
In .net2.0 on Windows XP x86 and x64, this worked great, no issues.
We recently upgraded to .net4.0 (4.0.3 specifically) and now this code is failing in a very specific way. (Fails on XP x86 and x64, Win7 works fine)
If the method is executed by the main GUI thread in an event handler, it works fine.
If however this method is executed in a new Thread, or by the ThreadPool, it fails with an Access Denied error.
This application is being run as an administrator and accessing the local computer wmi provider.
I would guess that there is some kind of thread security new in .net4 that I have not satisfied or something like that.
Here is some sample code which illustrates the problem.
This was from a form with 3 buttons, the handlers are at the end and illustrates when it works and when it fails.
It saves a copy of all Windows event logs to ..\Log folder.
Compiling the below with target framework of .Net2.0, all three calling methods work fine.
Compiling with target framework of .Net4.0.3, and the last 2 calling methods fail with Access Denied.
What am I missing here?
public void ExportWindowsEventLogs(object ignored)
{
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_NTEventlogFile");
searcher.Scope.Options.EnablePrivileges = true; // Defaults to False which gives AccessDenied errors so
foreach (ManagementObject oEventLogFile in searcher.Get())
{
string LogName = oEventLogFile["LogfileName"] as string;
if (!String.IsNullOrEmpty(LogName))
{
// Obtain in-parameters for the method
ManagementBaseObject inParams =
oEventLogFile.GetMethodParameters("BackupEventlog");
// Add the input parameters.
string strPath = Application.StartupPath + #"\..\Log\" + LogName + "-Exported.Evt";
inParams["ArchiveFileName"] = strPath;
//Delete the old backup if it exists, as the WMI BackupEventLog method does not overwrite old files.
FileInfo backupFile = new FileInfo(strPath);
if (!backupFile.Directory.Exists)
backupFile.Directory.Create();
else if (backupFile.Exists)
backupFile.Delete();
InvokeMethodOptions options = new InvokeMethodOptions();
// Execute the method and obtain the return values.
//FAILS ON NEXT LINE WITH ACCESS DENIED ERROR,
//but oEventLogFile.ToString() works fine.
ManagementBaseObject outParams =
oEventLogFile.InvokeMethod("BackupEventlog", inParams, null);
object returnValue = oEventLogFile.InvokeMethod("BackupEventlog", new object[] { strPath });
}
}
}
catch(Exception err)
{
MessageBox.Show("An error occurred while trying to execute the WMI method: " + err.ToString());
}
}
private void button1_Click(object sender, EventArgs e)
{
//WORKS
ExportWindowsEventLogs(null);
}
private void button2_Click(object sender, EventArgs e)
{
//Access Denied error
Thread workerThread = new Thread(new ParameterizedThreadStart(ExportWindowsEventLogs));
workerThread.Priority = ThreadPriority.Lowest;
workerThread.Start(true);
}
private void button3_Click(object sender, EventArgs e)
{
//Access Denied error
System.Threading.ThreadPool.QueueUserWorkItem(ExportWindowsEventLogs, null);
}
UPDATE:
This appears to be directly related to the Thread.ApartmentState setting of the executing thread.
By default threads are created with ApartmentState.MTA, whereas the main GUI thread is an STA thread.
In .Net 4.0, My WMI code works when running in an STA thread, but does not work on the MTA threads. In .Net 2.0, it works in both.
Any ideas how to get it to work in an MTA thread on .Net 4.0? I'd like to use the threadpool to run this.

oracle change notification

I am trying to get a test application to work using Oracle Change Notification with C#, but I am not receiving the callback notification in my application. Oracle DB version is 11.2.0.1.0. Oracle.DataAccess v.2.112.30. I can confirm the query gets registered in Oracle by viewing SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES. However, nothing ever appears in SYS.DBA_CHANGE_NOTIFICATION_REGS.
The registration persists until I commit a transaction on the table. The registration disappears after a few seconds after the commit and my application does not received the notification.
I have made sure my computer is listening on the correct port and have even tried turning off any firewall that could be blocking the port.
I do have GRANT CHANGE NOTIFICATION to MYSCHEMA, GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO MYSCHEMA, and the JOB_QUEUE_PROCESSES is set to 1.
Questions:
1) Should the registration be visible in SYS.DBA_CHANGE_NOTIFICATION_REGS and, if so, what could be causing it not to be when it is visible in SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES?
2) What could be causing the registration to disappear after a commit?
3) What could be causing the failure of the notification to my application?
Here is the C# code I am using and it is basically the same as from the Oracle website:
using System;
using System.Threading;
using System.Data;
using Oracle.DataAccess.Client;
namespace NotifyTest
{
public class Program
{
public static bool IsNotified = false;
public static void Main(string[] args)
{
string constr = "User Id=mySchema;Password=myPassword;Data Source=myOracleInstance";
OracleDependency dep = null;
try
{
using (var con = new OracleConnection(constr))
{
Console.WriteLine("Registering query...");
var cmd = new OracleCommand("select * from mySchema.NOTIFY_TEST", con);
con.Open();
OracleDependency.Port = 1005;
dep = new OracleDependency(cmd);
dep.OnChange += OnMyNotificaton;
int queryRegistered = cmd.ExecuteNonQuery();
// If oracle returns -1, then the query is successfully registered
if (queryRegistered == -1)
{
Console.WriteLine("Query Registered...");
Console.WriteLine("Listening for Callback...");
}
else
{
Console.WriteLine("There was an error...");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
// Loop while waiting for notification
while (IsNotified == false)
{
Thread.Sleep(100);
}
}
public static void OnMyNotificaton(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Table change notification event is raised");
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Info.ToString());
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Type.ToString());
IsNotified = true;
}
}
}
Just wanted to provide an update as to how I resolved this issue. I changed my Oracle.DataAccess.dll from v.2.112.3.0 to v.2.112.1.2 and it works fine.
In SYS.CHNF$_REG_INFO attribute QOSFLAGS there is QOS_DEREG_NFY, which specifies that the database should unregister the registration on the first notification.
Not sure but the value on job_queue_processes (1) is a bit low. Oracle performs all kinds of maintenance and event handling tasks internally. For this they also use Job slaves. Raise job_queue_processes (default 1000) and check Troubleshooting CQN Registrations

Categories

Resources