I have a process that is to be used to load data from various sources to a SQL Server database. Within the process, I have several methods that each consume file data and return a DataTable object. Depending on the type of data to be loaded, one of these methods is called for any single run of the process.
All of the DataTable objects created by these methods are consumed by the same target method, which transfers the data to SQL Server. This has led to some duplication of code:
if (useDT == 1)
{
using (DataTable dt = MakeDT1())
{
ConsumeDT(dt);
}
}
if (useDT == 2)
{
using (DataTable dt = MakeDT2())
{
ConsumeDT(dt);
}
}
(Simplified for clarity, real world names are descriptive)
I'd like to avoid this if at all possible. Is it possible to pre-calculate the correct method to call to generate the DataTable, then call ConsumeDT(dt) just once? E.g.
switch (useDT)
{
case 1
dtCall = MakeDT1()
break;
case 2
dtCall = MakeDT2()
}
using (DataTable dt = dtCall)
//etc
Thanks in advance, Iain
Write a little helper method that returns the correct kind of DT object:
private DataTable makeDt(bool useDT)
{
return useDT ? MakeDT1() : MakeDT2();
}
And then call that in the using like so:
using (var dt = makeDt(useDt))
{
ConsumeDT(dt);
}
This has the advantage of assigning the disposable dt inside a using making it unlikely that someone will write code that could cause a leak.
You can leave out the using block, you'll just have to make sure to correctly dispose the object in the end:
DataTable dt;
switch(useDT) {
case 1: dt = MakeDT1(); break;
case 2: dt = MakeDT2(); break;
}
try {
ConsumeDt(dt);
} finally {
dt.Dispose();
}
Related
I have an old application that I wrote in Access VBA, the time has come to upgrade the code and the company decided to go with C# since we use it the most. My question is following, I have this code in VBA that works great,
Set RS2 = Db.OpenRecordset("Select * FROM TTable WHERE ID="&Forms![test]![SifraFirme]&")
su = RS2.RecordCount
RS2.MoveFirst
Do While Not RS2.EOF
//lines of code
RS3.MoveNext
Loop
RS3.Close
Now my question is, is there a C# command similar to Do While Not RS.EOF, any literature or examples would be highly appreciated. Just a nudge in the right direction because it has become frustrating. The main point of code above is to go through the table and filter the data and write it to XML (predefined structure) based on ID once he is done with first, move on to the second, and ...
Thank you,
Answering to:
The main point of code above is to go through the table and filter the
data and write it to XML
You can read database table to some DataSet, using OleDbDataAdapter from System.Data namespace. Then easily work with filled DataSet or instantly get its XML representation by GetXml method:
static void Main(string[] args)
{
// Note about set Prefer 32-bit app version of your C# app to use Jet.OLEDB provider
var connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=YourDBPath";
var query = "Select * FROM TTable";
// Introducing our DataSet
var dataSet = new System.Data.DataSet();
using (var connection = new System.Data.OleDb.OleDbConnection(connectionString))
{
var command = new System.Data.OleDb.OleDbCommand(query, connection);
try
{
connection.Open();
using (var dataAdapter = new System.Data.OleDb.OleDbDataAdapter(command))
{
// Fill DataSet
dataAdapter.Fill(dataSet);
}
// Get XML representation of DataSet and save to XML file
System.IO.File.WriteAllText(#"TTable.xml", dataSet.GetXml());
// Or if need to filter data before save - read through DataSet
var TTable = dataSet.Tables["TTable"];
foreach (var row in TTable.Rows.Cast<System.Data.DataRow>().ToArray()) // using System.Linq needed
{
}
}
catch (System.Exception ex)
{
// Handle exception in some way
System.Console.WriteLine(ex.Message);
}
}
System.Console.ReadKey();
}
C# has the XMLWriter class and you can use the SQL classes for querying and reading the information.
The while loop in C# would be something like this:
while (!RS2.EOF)
{
//lines of code
RS2.MoveNext();
}
The ! is the Logical negation operator.
ADO.NET has the DataSet class which works with data in a way that is similar to a RecordSet in VBA.
See Microsoft's documentation on DataSet
I have a datatable containing file paths which I am passing via viewstate (referencing, via a linkbutton, an index in this table), wanting to then use the path from the table to construct a HTTP filetransfer. (So 3 cols; name, path and index)
I am unable to successfully retrieve the datatable once saved in viewstate;
ViewState["varFiles"] = filedata;
(When page is originally constructed, then after postback:)
if (!IsPostBack) { SetupSession(); newpopfiles(); }
else { { if (ViewState["varFiles"] != null) { DataTable filedata = new DataTable(); filedata = (DataTable)Session["varFiles"]; } } }
From what I understand this should pull back filedata as a table in exactly the same form as before postback. Is this correct?
When subsequently referencing the table I get a null reference exception. Any ideas?
Many thanks,
Dan
It sounds like you're almost there, just need to be a bit more consistent with using the same storage mechanism :)
The bit to save the DataTable into your session, probably in OnInit() or PageLoad():
DataTable myDataTable = //... fill it in somehow
Session["varFiles"] = myDataTable;
The bit to read the DataTable after postback:
if (!IsPostBack)
{
SetupSession();
newpopfiles();
}
else
{
DataTable filedata = Session["varFiles"] as DataTable;
if (filedata != null)
{
//... do something
}
}
I'm trying to create a method which when passed a datatable reference with pingable host names, tries to ping each of the hosts and then change the value of corresponding column and row depending on ping success.
However i cannot use references in Parallel.ForEach method. Is there any way i could make this work?
Here's my code:
public void checkON(ref DataTable PCS)
{
Parallel.ForEach(PCS.AsEnumerable(), pc =>
{
string loopIp = pc["Name"].ToString();
if (PingIP(loopIp))
{
DataRow[] currentpc = PCS.Select("Name = '{0}'", loopIp);
currentpc[0]["Online"] = "ON";
}
else
{
DataRow[] currentpc = PCS.Select("Name = '{0}'", loopIp);
currentpc[0]["Online"] = "OFF";
}
}
);}
Unless code explicitly says that it is thread-safe, you should assume it is not - and therefore access must be synchronized. The ref in your code serves no purpose. Each pc is a DataRow, so you can access that directly:
string loopIp;
lock(someLockObject) {
loopIp = (string)pc["Name"];
}
string online = PingIP(loopIp) ? "ON" : "OFF";
lock(someLockObject) {
pc["Online"] = online;
}
where someLockObject is shared between all of the callers, because you can't make assumptions about the threading model:
object someLockObject = new object();
Parallel.ForEach(PCS.AsEnumerable(), pc =>
{ ... });
In particular, you can't just lock the row because DataTable doesn't store data in rows (it stores it in columns; no, really).
I'm trying to fetch data from DB with optional overload to pass the connection. I could do it in two ways.
public DataTable GetData()
{
using (SqlConnection con = new SqlConnection("..."))
{
return GetData(con);
}
}
public DataTable GetData(SqlConnection con)
{
// fetch data
return dtData;
}
or
public DataTable GetData(SqlConnection con=null)
{
bool OpenCon = false;
if (con == null)
{
con = new SqlConnection("...");
OpenCon = true;
}
try
{
// fetch data
}
finally
{
if (OpenCon)
con.Close();
}
return dtData;
}
The first case seems impressive. However, I am getting tons of methods for each transaction. In the second case, lots of code need to be written in each method as there is no way to use "using" block in this case.
The situation is still worse with other transactions like update or delete, since I need to have another overload to pass the transaction.
Is there a better way?
1st one is the best choice
public DataTable GetData()
{
using (SqlConnection con = new SqlConnection("..."))
{
return GetData(con);
}
}
public DataTable GetData(SqlConnection con)
{
// fetch data
return dtData;
}
Here you have Object oriented implementation, providing specific boundry as well as removal of object(using statement to destory objects ) are all there which is a good programmaing.
I have a service which continuously writes data in a separate thread into SQL database.Now from the same service if i am trying to read from the same table, since i already am writing into it,I get this exception : There is already an open DataReader associated with this Command which must be closed first.
So can anyone help me how to do this simultaneously?
Here s my code for reading data:
public Collection ReadData(string query)
{
{
_result = new Collection<string[]>();
string[] tempResult;
SqlDataReader _readerRead;
using (_command = new SqlCommand(query, _readConnection))
{
_readerRead = _command.ExecuteReader();
while (_readerRead.Read())
{
tempResult = new string[4];
tempResult[0] = _reader[0].ToString();
tempResult[1] = _reader[1].ToString();
tempResult[2] = _reader[2].ToString();
tempResult[3] = _reader[3].ToString();
_result.Add(tempResult);
//Console.WriteLine("Name : {0} Type : {1} Value : {2} timestamp : {3}", _reader[0], _reader[1], _reader[2], _reader[3]);
}
if (_readerRead != null)
{
_readerRead.Close();
}
_readConnection.Close();
return _result;
}
}
}
and here it is for writing to it :
public void WriteData(Collection<TagInfo> tagInfoList)
{
int i = 0;
for (i = 0; i < tagInfoList.Count; i++)
{
using( _command = new SqlCommand(insert statement here)
{
_command.Parameters.AddWithValue("Name", tagInfoList[i].Name);
_command.Parameters.AddWithValue("Type", tagInfoList[i].TagType);
_command.Parameters.AddWithValue("Value", tagInfoList[i].Value);
_reader = _command.ExecuteReader();
if (_reader != null)
{
_reader.Close();
}
}
}
}
You need a different SQLConnection to the database for your writer. You cannot use the same db connection for both.
Although its possible to do, using a separate connection I would question why you need to do this.
If you are reading and writing data to one table in the same service you will be placing unnecessary load on one SQL table, and depending on the number of queries you intend to make this could cause you problems. If you already have this data (in a different thread) why not Marshall the data from the background thread to where you need it as you write it into the database, and you don't need to read the data anymore.
However.... it is difficult to give an fair answer without seeing the code/what you are looking to achieve.