I am trying to read the messages that SQL Server normally returns in SSMS in the messages tab. Specifically information from the SET STATISTICS IO and TIME. The code below works but does not actually give this output. Any help is greatly appreciated. Thank you.
using System;
using System.Collections;
using System.Data.SqlClient;
using System.Data;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var cn2 = new MedusaPerf.ConnectionStringBuilder().GetTrustedConnectionString("localhost", "AdventureWorks2012", false);
//var cn2 = new MedusaPerf.ConnectionStringBuilder().GetStandardConnectionString("localhost", "AdventureWorks2012", "testuser", "pass", false);
string infoMessageText = "";
try
{
var cmd = "SET STATISTICS IO ON; SET STATISTICS TIME ON; SELECT TOP(5) DatabaseLogID, PostTime, Event FROM [dbo].[DatabaseLog];";
cn2.StatisticsEnabled = true;
//cn2.InfoMessage += new SqlInfoMessageEventHandler(InfoMessageHandler);
cn2.InfoMessage += delegate(object sender, SqlInfoMessageEventArgs e)
{
infoMessageText += e.Message.ToString();
};
var daDataOutput = new SqlDataAdapter(cmd, cn2);
DataTable dtOutput = new DataTable();
daDataOutput.Fill(dtOutput);
foreach (DataRow i in dtOutput.Rows)
{
string dataRowOutput = "";
for (int j = 0; j < dtOutput.Columns.Count; j++)
{
dataRowOutput = dataRowOutput + i[j].ToString();
}
Console.WriteLine(dataRowOutput);
}
IDictionary d = cn2.RetrieveStatistics();
string[] keys = new string[d.Count];
d.Keys.CopyTo(keys,0);
for (int x = 0; x < d.Count; x++)
{
Console.WriteLine("{0}\t{1}",keys[x], (long)d[keys[x]]);
}
Console.WriteLine("Success ");
}
catch (Exception)
{
throw;
}
Console.WriteLine(infoMessageText);
Console.WriteLine("Hit Enter to Continue");
System.Console.ReadKey();
}
static void InfoMessageHandler(object sender, SqlInfoMessageEventArgs e)
{
string myMsg = e.Message;
Console.WriteLine(e.Message);
}
}
}
Here is the output:
13/14/2012 1:14:18 PMCREATE_TABLE
23/14/2012 1:14:18 PMALTER_TABLE
53/14/2012 1:14:18 PMCREATE_TYPE
63/14/2012 1:14:18 PMCREATE_TYPE
213/14/2012 1:14:19 PMCREATE_XML_SCHEMA_COLLECTION
ExecutionTime 46
UnpreparedExecs 1
SelectRows 5
Prepares 0
BuffersSent 1
PreparedExecs 0
SelectCount 2
IduRows 0
BytesReceived 911
Transactions 0
IduCount 0
ServerRoundtrips 1
CursorOpens 0
SumResultSets 1
NetworkServerTime 0
ConnectionTime 0
BytesSent 262
BuffersReceived 1
Success
Hit Enter to Continue
The resolution ultimately was that Fill method does not trigger InfoMessage method thus I was unable to capture the messages. I found another post on StackOverflow that addresses this reasoning. Below is the working code. https://stackoverflow.com/a/2914981/62511
using System;
using System.Collections;
using System.Data.SqlClient;
using System.Data;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var cn2 = new MedusaPerf.ConnectionStringBuilder().GetTrustedConnectionString("localhost", "AdventureWorks2012", false);
//var cn2 = new MedusaPerf.ConnectionStringBuilder().GetStandardConnectionString("localhost", "AdventureWorks2012", "testuser", "pass", false);
string infoMessageText = "";
var cmd = "SET STATISTICS IO ON; SET STATISTICS TIME ON; SELECT DatabaseLogID, PostTime, Event FROM [dbo].[DatabaseLog];";
cn2.StatisticsEnabled = true;
cn2.InfoMessage += delegate(object sender, SqlInfoMessageEventArgs e)
{
infoMessageText += e.Message.ToString();
};
cn2.Open();
try
{
SqlCommand comm = new SqlCommand(cmd, cn2);
comm.ExecuteNonQuery();
IDictionary d = cn2.RetrieveStatistics();
string[] keys = new string[d.Count];
d.Keys.CopyTo(keys, 0);
for (int x = 0; x < d.Count; x++)
{
Console.WriteLine("{0}\t{1}", keys[x], (long)d[keys[x]]);
}
Console.WriteLine("Success ");
}
catch (Exception)
{
throw;
}
//Console.Write(conn_InfoMessage());
cn2.Close();
Console.WriteLine(infoMessageText);
Console.WriteLine("Hit Enter to Continue");
System.Console.ReadKey();
}
}
}
first of all I don't think the
SET STATISTICS IO ON
is needed ... io statistics seem to be controlled be the StatisticsEnabled property of the SqlConnection class ...
the thing with the time statistics is really strange ... I had the same problem ... I found out that when you insert a print statement between SET STATISTICS TIME ON and SELECT ... the handler for InfoMessage gets called like it should be ... once for the print statement and once for the statistics ...
ps: tried to put the complete statement here but could not submit ("an error occurred submitting the answer") ... hope you can find out yourself ...
Related
I'm new to C#, and I am supposed to be demonstrating creating arrays and processing the contents of the arrays. Whenever I press calculate I get the same numbers every time. I am not sure what I am missing? I changed double to decimals thinking that might help change things, but no matter what I try to fix it still comes up the same. Any help in this is appreciated. I also had to save a .txt file into the same folder as the bin/debug for this project. Im instructed that this is supposed to read the files content into the array of double or decimal. the folder just has a few numbers with decimals (1260.07, etc)
heres the code I have so far.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace WindowsFormsAppAssignment6
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void analyzeBtn_Click(object sender,System.EventArgs e)
{
string file = "Sales.txt";
string salesValue;
Decimal[] sales = new decimal[7];
int count = 0;
decimal maxSales = Decimal.MinValue;
decimal minSales = Decimal.MaxValue;
decimal totalSales = 0;
try
{
StreamReader dataStream = new StreamReader(file);
salesValue = dataStream.ReadLine();
var total = sales.Sum();
var average = sales.Average();
var high = sales.Max();
var low = sales.Min();
while (salesValue != null)
{
sales[count] = Convert.ToDecimal(salesValue);
totalSales += sales[count];
if (sales[count] > maxSales)
maxSales = sales[count];
if (sales[count] < minSales)
minSales = sales[count];
count++;
salesValue = dataStream.ReadLine();
}
dataStream.Close();
for (int item = 0; item < 7; item++)
salesListBox.Items.Add(sales[item]);
totalLbl.Text = totalSales + "";
avgLabel.Text = totalSales + "";
highSalesLabel.Text = maxSales + "";
smallSalesLabel.Text = minSales + "";
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void label3_Click(object sender, EventArgs e)
{
}
private void exitBtn_Click(object sender, EventArgs e)
{
this.Close();
}
}
}'''
Heres what populates every time.
The problem I think is with your error handling or the format of your document. Convert will actually throw an exception which will kill your entire loop if it cannot parse the current line. Say you had a simple line break in your file that created an empty line, you would try to parse it, an error is thrown, your code does nothing after it. Since I didnt have your file and I really do not feel like using visual studio instead of vscode, I replaced your streamreader with a stringreader (reads a string instead of a file but almost exactly the same) and i replaced the form with simple console writes. Outside of those changes, I gave myself some extra room in the array size since it was throwing exceptions and i wrapped the inside of the while loop in a try catch for format exceptions which is what Convert throws. After I made those changes, the outputs all worked properly. If you still dont see the values, then it is a problem with how you are using windows forms not your codes logic itself.
public static void Main()
{
Console.WriteLine("Hello World");
analyzeBtn_Click();
}
public static string SalesTxt = #"
1260.07
4002.52
267.21
15773.223
66655.2
555.0
7322
";
private static void analyzeBtn_Click()
{
string salesValue;
Decimal[] sales = new decimal[8];
int count = 0;
decimal maxSales = Decimal.MinValue;
decimal minSales = Decimal.MaxValue;
decimal totalSales = 0;
try
{
StringReader dataStream = new StringReader(SalesTxt);
salesValue = dataStream.ReadLine();
var total = sales.Sum();
var average = sales.Average();
var high = sales.Max();
var low = sales.Min();
while (salesValue != null)
{
try {
sales[count] = Convert.ToDecimal(salesValue);
totalSales += sales[count];
if (sales[count] > maxSales)
maxSales = sales[count];
if (sales[count] < minSales)
minSales = sales[count];
count++;
} catch (FormatException) {Console.WriteLine("\tException: '"+ salesValue + "'"); }
salesValue = dataStream.ReadLine();
}
dataStream.Close();
string listItems = string.Join(",\n", sales);
Console.WriteLine(listItems);
Console.WriteLine(totalSales);
Console.WriteLine(maxSales);
Console.WriteLine(minSales);
} catch (Exception ex) {
Console.WriteLine(ex);
}
}
Output:
Hello World
Exception: ''
Exception: ' '
1260.07,
4002.52,
267.21,
15773.223,
66655.2,
555.0,
7322,
0
95835.223
66655.2
267.21
EDIT:
In light of discussion, I have moved the string into a file with the appropriate name and used StreamReader just as you originally did. Using the same code as above just slightly modified, I was still able to get the correct output.
This is a basic c# application but I am quite rusty. I am just going to start with showing you my code
using System;
using System.Data.SqlClient;
using System.Text;
namespace DatabaseAdder
{
class Program
{
static void Main(string[] args)
{
int RUId = 0;
int QuestionId = 0;
DateTime Date = DateTime.Now;
string QuestionWhenAnswered ;
string QuestionResponse;
int Accepted;
string AssignedWorkStation;
string CompleteToken;
try
{for (int i = 0; i < 300; i++) {
QuestionId ++;
QuestionIncrementInSetsOfTwelve(QuestionId);
Console.WriteLine(i );
Console.WriteLine( QuestionId);
Random rand = new Random();
// Build connection string
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "localhost"; // update me
builder.UserID = "sa"; // update me
builder.Password = "Mypassword123"; // update me
builder.InitialCatalog = "CDA";
// Connect to SQL
Console.Write("Connecting to SQL Server ... ");
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
connection.Open();
var sql = "INSERT INTO QuestionResponses(RUId, QuestionId,Date,QuestionWhenAnswered,QuestionResponse,Accepted,AssignedWorkStation,CompleteToken)" +
" VALUES(#RUId, #QuestionId,#Date,#QuestionWhenAnswered,#QuestionResponse,#Accepted,#AssignedWorkStation,#CompleteToken)";
using (var cmd = new SqlCommand(sql, connection))
{
cmd.Parameters.AddWithValue("#RUId", "1" );
cmd.Parameters.AddWithValue("#QuestionId", "1");
cmd.Parameters.AddWithValue("#Date", DateTime.Now);
cmd.Parameters.AddWithValue("#QuestionWhenAnswered", "sam");
cmd.Parameters.AddWithValue("#QuestionResponse", "sam");
cmd.Parameters.AddWithValue("#Accepted", "1");
cmd.Parameters.AddWithValue("#AssignedWorkStation", "sam");
cmd.Parameters.AddWithValue("#CompleteToken", "sam");
cmd.ExecuteNonQuery();
}
}
}
}
catch (SqlException e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("All done. Press any key to finish...");
Console.ReadKey(true);
}
static int QuestionIncrementInSetsOfTwelve(int questionId)
{
if(questionId < 12)
{
questionId = 0;
}
else
{
}
return questionId;
}
}
}
The questionincrementinsetsoftwelve is not changing the value when it is called even when I have debugged and can watch that its value is over 12 but it is still not setting this back to 0.
I understand that there is probably something very small I am overlooking so be easy on my ego.
This bit
QuestionId ++;
QuestionIncrementInSetsOfTwelve(QuestionId);
Should be
QuestionId ++;
QuestionId = QuestionIncrementInSetsOfTwelve(QuestionId);
integers are passed by value, not by reference. So a new integer that has the same value as QuestionId is being passed to the method, and that new integer is being returned from it. You have to assign the result. The behavior you are looking for can be achieved by using the ref keyword but isn't needed so long as we assign the result.
You're passing QuestionIncrementInSetsOfTwelve() a value. It's not updating the parameter, it's taking it in, doing "stuff," and returning a new value.
If you want to alter the parameter passed in, use the ref keyword.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref
Example from microsoft:
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45
Notice how this returns nothing? It updates the argument passed in, because it was passed by ref.
This question already has answers here:
How to deal with cross-thread access exceptions?
(3 answers)
Closed 5 years ago.
I have the the following method to show in a gridview (dgJEARequests) a customized selection of items, as you can see from the images. The user will select the items that will send the parameters to the query whereStatement. But when many checkboxes (items) are marked or selected, it will take some time to load all the data in the dgJEARequests, let's say, 5000 records, or less. So, I decided to add a WaitProcessing (a form with a progress bar). When I click the search button btnSearchSelection, I get this error:
An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control 'dgJEARequests' accessed from a thread other than the thread it was created on
private void ShowOnlyCustomSelection()
{
String whereStatement = "";
List<String> fieldList = new List<String>();
string getStatusName = "";
string _getStatusName = "";
//loop through all the checkboxes
for (int i = 0; i < count; i++)
{
if (_cbStatus[i].Checked)
{
//getStatusName should give all selected options like this: 'New', 'Started', 'Accepted', etc., then pass it to whereStatement
_getStatusName += ("'" + _cbStatus[i].Text + "'" + ",").TrimEnd();
}
}
//trims the last comma (,)
getStatusName = _getStatusName.TrimEnd(',');
//textBox1.Text = _getStatusName.TrimEnd(','); //--->>this is for testing
////////////
if (getStatusName == "" || getStatusName == null)
{
{
MessageBox.Show("You have not selected your filter(s)!", "Filter Result", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
else
{
//Build WHERE Statement
fieldList.Add("RequestStatus.StatusName IN (" + getStatusName + ")");
if (fieldList.Count > 0)
{
for (int x = 0; x < fieldList.Count; x++)
{
if (x == 0)
{
whereStatement = fieldList[x];
}
else
{
whereStatement = whereStatement + " AND " + fieldList[x];
}
}
//Seach for Requests
jeaRequests = itServices.getRequestsBySQLStatement(whereStatement);
//dgJEARequests.DataSource = jeaRequests;
requestList = new List<ITWebService.Requests>(jeaRequests.ToList());
dgJEARequests.DataSource = requestList;
dgJEARequests.ClearSelection();
lblCountOfRequests.Text = requestList.Count.ToString().ToString() + " requests loaded";
}
else
{
if (chkMine.Checked)
{
jeaRequests = itServices.getRequestsByAssignedToAndStatusID(JEAUser.UserName, "0");
//dgJEARequests.DataSource = jeaRequests;
requestList = new List<ITWebService.Requests>(jeaRequests.ToList());
dgJEARequests.DataSource = requestList;
dgJEARequests.ClearSelection();
}
else
{
jeaRequests = itServices.getRequestsBySQLStatement("Requests.ID > 0");
//dgJEARequests.DataSource = jeaRequests;
requestList = new List<ITWebService.Requests>(jeaRequests.ToList());
dgJEARequests.DataSource = requestList;
dgJEARequests.ClearSelection();
}
}
}
}
This is the code for the progressbar:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace JEAProjectManager
{
public partial class WaitProcessing : Form
{
public Action Worker
{
get;
set;
}
public WaitProcessing(Action worker)
{
InitializeComponent();
if (worker == null)
throw new ArgumentNullException();
Worker = worker;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Task.Factory.StartNew(Worker).ContinueWith(t =>
{
this.Close();
},
TaskScheduler.FromCurrentSynchronizationContext());
}
}
}
This other is for when I click the search button:
private void btnSearchSelection_CheckedChanged(object sender, EventArgs e)
{
//ShowOnlyCustomSelection();
//WaitProcessing processing = new WaitProcessing();
using (WaitProcessing processing = new WaitProcessing(ShowOnlyCustomSelection))
{
processing.ShowDialog(this);
}
}
The query parameters are sent to a Webservice. What can I do to solve this problem? I am customizing this application. I am not the original developer, but it is my task to make it better. I know there are some similar errors out there, but different scenarios.
Screenshots:
Doing my selection
Passing parameters for query
ProgressBar
Error Message
possible duplicate :-/
How to deal with cross-thread access exceptions?
you need to ensure, that the access thread is initialized
It looks like the answer was easy, but took me a while to figure it out. I am not sure if there are better solutions for this, but I had to enclose those components in lambda expression like:
lblCountOfRequests.Text = requestList.Count.ToString().ToString() + " requests loaded";
Or
dgJEARequests.Invoke(new Action(() => dgJEARequests.DataSource = requestList));
I thought I was trying to do something very simple. I just want to report a running number on the screen so the user gets the idea that the SQL Stored Procedure that I'm executing is working and that they don't get impatient and start clicking buttons.
The problem is that I can't figure out how to actually call the progress reporter for the ExecutNonQueryAsync command. It gets stuck in my reporting loop and never executes the command but, if I put it after the async command, it will get executed and result will never not equal zero.
Any thoughts, comments, ideas would be appreciated. Thank you so much!
int i = 0;
lblProcessing.Text = "Transactions " + i.ToString();
int result = 0;
while (result==0)
{
i++;
if (i % 500 == 0)
{
lblProcessing.Text = "Transactions " + i.ToString();
lblProcessing.Refresh();
}
}
// Yes - I know - the code never gets here - that is the problem!
result = await cmd.ExecuteNonQueryAsync();
The simplest way to do this is to use a second connection to monitor the progress, and report on it. Here's a little sample to get you started:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Samples.SqlServer
{
public class SessionStats
{
public long Reads { get; set; }
public long Writes { get; set; }
public long CpuTime { get; set; }
public long RowCount { get; set; }
public long WaitTime { get; set; }
public string LastWaitType { get; set; }
public string Status { get; set; }
public override string ToString()
{
return $"Reads {Reads}, Writes {Writes}, CPU {CpuTime}, RowCount {RowCount}, WaitTime {WaitTime}, LastWaitType {LastWaitType}, Status {Status}";
}
}
public class SqlCommandWithProgress
{
public static async Task ExecuteNonQuery(string ConnectionString, string Query, Action<SessionStats> OnProgress)
{
using (var rdr = await ExecuteReader(ConnectionString, Query, OnProgress))
{
rdr.Dispose();
}
}
public static async Task<DataTable> ExecuteDataTable(string ConnectionString, string Query, Action<SessionStats> OnProgress)
{
using (var rdr = await ExecuteReader(ConnectionString, Query, OnProgress))
{
var dt = new DataTable();
dt.Load(rdr);
return dt;
}
}
public static async Task<SqlDataReader> ExecuteReader(string ConnectionString, string Query, Action<SessionStats> OnProgress)
{
var mainCon = new SqlConnection(ConnectionString);
using (var monitorCon = new SqlConnection(ConnectionString))
{
mainCon.Open();
monitorCon.Open();
var cmd = new SqlCommand("select ##spid session_id", mainCon);
var spid = Convert.ToInt32(cmd.ExecuteScalar());
cmd = new SqlCommand(Query, mainCon);
var monitorQuery = #"
select s.reads, s.writes, r.cpu_time, s.row_count, r.wait_time, r.last_wait_type, r.status
from sys.dm_exec_requests r
join sys.dm_exec_sessions s
on r.session_id = s.session_id
where r.session_id = #session_id";
var monitorCmd = new SqlCommand(monitorQuery, monitorCon);
monitorCmd.Parameters.Add(new SqlParameter("#session_id", spid));
var queryTask = cmd.ExecuteReaderAsync( CommandBehavior.CloseConnection );
var cols = new { reads = 0, writes = 1, cpu_time =2,row_count = 3, wait_time = 4, last_wait_type = 5, status = 6 };
while (!queryTask.IsCompleted)
{
var firstTask = await Task.WhenAny(queryTask, Task.Delay(1000));
if (firstTask == queryTask)
{
break;
}
using (var rdr = await monitorCmd.ExecuteReaderAsync())
{
await rdr.ReadAsync();
var result = new SessionStats()
{
Reads = Convert.ToInt64(rdr[cols.reads]),
Writes = Convert.ToInt64(rdr[cols.writes]),
RowCount = Convert.ToInt64(rdr[cols.row_count]),
CpuTime = Convert.ToInt64(rdr[cols.cpu_time]),
WaitTime = Convert.ToInt64(rdr[cols.wait_time]),
LastWaitType = Convert.ToString(rdr[cols.last_wait_type]),
Status = Convert.ToString(rdr[cols.status]),
};
OnProgress(result);
}
}
return queryTask.Result;
}
}
}
}
Which you would call something like this:
class Program
{
static void Main(string[] args)
{
Run().Wait();
}
static async Task Run()
{
var constr = "server=localhost;database=tempdb;integrated security=true";
var sql = #"
set nocount on;
select newid() d
into #foo
from sys.objects, sys.objects o2, sys.columns
order by newid();
select count(*) from #foo;
";
using (var rdr = await SqlCommandWithProgress.ExecuteReader(constr, sql, s => Console.WriteLine(s)))
{
if (!rdr.IsClosed)
{
while (rdr.Read())
{
Console.WriteLine("Row read");
}
}
}
Console.WriteLine("Hit any key to exit.");
Console.ReadKey();
}
}
Which outputs:
Reads 0, Writes 0, CPU 1061, RowCount 0, WaitTime 0, LastWaitType SOS_SCHEDULER_YIELD, Status running
Reads 0, Writes 0, CPU 2096, RowCount 0, WaitTime 0, LastWaitType SOS_SCHEDULER_YIELD, Status running
Reads 0, Writes 0, CPU 4553, RowCount 11043136, WaitTime 198, LastWaitType CXPACKET, Status suspended
Row read
Hit any key to exit.
You're not going to be able to get ExecuteNonQueryAsync to do what you want here. To do what you're looking for, the result of the method would have to be either row by row or in chunks incremented during the SQL call, but that's not how submitting a query batch to SQL Server works or really how you would want it to work from an overhead perspective. You hand a SQL statement to the server and after it is finished processing the statement, it returns the total number of rows affected by the statement.
Do you just want to let the user know that something is happening, and you don't actually need to display current progress?
If so, you could just display a ProgressBar with its Style set to Marquee.
If you want this to be a "self-contained" method, you could display the progress bar on a modal form, and include the form code in the method itself.
E.g.
public void ExecuteNonQueryWithProgress(SqlCommand cmd) {
Form f = new Form() {
Text = "Please wait...",
Size = new Size(400, 100),
StartPosition = FormStartPosition.CenterScreen,
FormBorderStyle = FormBorderStyle.FixedDialog,
MaximizeBox = false,
ControlBox = false
};
f.Controls.Add(new ProgressBar() {
Style = ProgressBarStyle.Marquee,
Dock = DockStyle.Fill
});
f.Shown += async (sender, e) => {
await cmd.ExecuteNonQueryAsync();
f.Close();
};
f.ShowDialog();
}
That is an interesting question. I have had to implement similar things in the past. In our case the priority was to:
Keep client side responsive in case the user doesn't want to stick around and wait.
Update the user of action and progress.
What I would do is use threading to run the process in the background like:
HostingEnvironment.QueueBackgroundWorkItem(ct => FunctionThatCallsSQLandTakesTime(p, q, s));
Then using a way to estimate work time I would increment a progress bar from client side on a clock. For this, query your data for a variable that gives you a linear relationship to the work time needed by FunctionThatCallsSQLandTakesTime.
For example; the number of active users this month drives the time FunctionThatCallsSQLandTakesTime takes. For each 10000 user it takes 5 minutes. So you can update your progress bar accordingly.
I'm wondering if this might be a reasonable approach:
IAsyncResult result = cmd2.BeginExecuteNonQuery();
int count = 0;
while (!result.IsCompleted)
{
count++;
if (count % 500 == 0)
{
lblProcessing.Text = "Transactions " + i.ToString();
lblProcessing.Refresh();
}
// Wait for 1/10 second, so the counter
// does not consume all available resources
// on the main thread.
System.Threading.Thread.Sleep(100);
}
I have two parts to my application which both does massive amount of insert and update respectively and because or poor managemenent, there's deadlock.
I am using entity framework to do my insert and update.
The following is my code for my TestSpool program. The purpose of this program is to insert x number of records with a given interval.
using System;
using System.Linq;
using System.Threading;
using System.Transactions;
namespace TestSpool
{
class Program
{
static void Main(string[] args)
{
using (var db = new TestEntities())
{
decimal start = 700001;
while (true)
{
using (TransactionScope scope = new TransactionScope())
{
//Random ir = new Random();
//int i = ir.Next(1, 50);
var objs = db.BidItems.Where(m => m.BidItem_Close == false);
foreach (BidItem bi in objs)
{
for (int j = 0; j <= 10; j++)
{
Transaction t = new Transaction();
t.Item_Id = bi.BidItemId;
t.User_Id = "Ghost";
t.Trans_Price = start;
t.Trans_TimeStamp = DateTime.Now;
start += 10;
db.Transactions.AddObject(t);
}
Console.WriteLine("Test Spooled for item " + bi.BidItemId.ToString() + " of " + 11 + " bids");
db.SaveChanges();
}
scope.Complete();
Thread.Sleep(5000);
}
}
}
}
}
}
The second part of the program is the testserverclass, the serverclass supposed to processed a huge amount of transactions from testspool and determined the highest amount of the transaction and update to another table.
using System;
using System.Linq;
using System.Transactions;
public class TestServerClass
{
public void Start()
{
try
{
using (var db = new TestServer.TestEntities())
{
while (true)
{
using (TransactionScope scope = new TransactionScope())
{
var objsItems = db.BidItems.Where(m => m.BidItem_Close == false);
foreach (TestServer.BidItem bi in objsItems)
{
var trans = db.Transactions.Where(m => m.Trans_Proceesed == null && m.Item_Id == bi.BidItemId).OrderBy(m => m.Trans_TimeStamp).Take(100);
if (trans.Count() > 0)
{
var tran = trans.OrderByDescending(m => m.Trans_Price).FirstOrDefault();
// TestServer.BidItem bi = db.BidItems.FirstOrDefault(m => m.BidItemId == itemid);
if (bi != null)
{
bi.BidMinBid_LastBid_TimeStamp = tran.Trans_TimeStamp;
bi.BidMinBid_LastBidAmount = tran.Trans_Price;
bi.BidMinBid_LastBidBy = tran.User_Id;
}
foreach (var t in trans)
{
t.Trans_Proceesed = "1";
db.Transactions.ApplyCurrentValues(t);
}
db.BidItems.ApplyCurrentValues(bi);
Console.WriteLine("Processed " + trans.Count() + " bids for Item " + bi.BidItemId);
db.SaveChanges();
}
}
scope.Complete();
}
}
}
}
catch (Exception e)
{
Start();
}
}
}
However, as both application con-currently runs, it will go into deadlock pretty fast randomly either from the first test or server application. How do i optimised my code for both side to prevent deadlocks ? I am expecting huge amount of inserts from the testspool application.
Since they work on the same data and get in each others way, I believe the cleanest way to do this would be to avoid executing these two at the same time.
Define a global static variable, or a mutex or a flag of some kind, maybe on the database. Whoever starts executing raises the flag, other one waits for the flag to come down. When flag comes down the other one raises the flag and starts executing.
To avoid long wait times on each class you can alter both your classes to process only a limited amount of records each turn. You should also introduce a maximum wait time for the flag. Maximum record limit should be chosen carefully to ensure that each class finishes its job in shorter time than max wait time.