I'm having issue with creating product code in recursion.
What I want to do is:
I enter code 1000
-If code exist in database regenerateCode(string code)
-else insert into database
Code:
(...)
if(codeExist)
CodeTB.Text = regenerateCode(string toParse); //toParse = 1000
string regenerateCode(string toParse)
{
string helper = "";
int parseCode = int.Parse(toParse);
helper = new string('0', 4 - parseCode.ToString().Length);
helper += parseCode + 1;
using (SqlConnection conn = new SqlConnection(cString.c_String))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT Product.productID FROM Product " +
"WHERE Product.PLU = '" + helper + "' ", conn))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
if (rdr.HasRows)
{
// if code still exist in database, regenerate it
regenerateCode(helper);
}
else
{
//else return code
return helper;
}
}
}
}
return helper;
}
Actually it works fine with example:
1000 (exist)
1001 (exist)
1002 (not exist, insert), but
when code = 1002, it goes into line with else {return helper;} and have no idea why going again to regenerateCode() method..
Any ideas?
It happens because you do nothing with the return value. You pass helper to the recursion, but do not place the return value anywhere. your method should look like this:
string regenerateCode(string toParse)
{
string helper = "";
int parseCode = int.Parse(toParse);
helper = new string('0', 4 - parseCode.ToString().Length);
helper += parseCode + 1;
using (SqlConnection conn = new SqlConnection(cString.c_String))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT Product.productID FROM Product " +
"WHERE Product.PLU = '" + helper + "' ", conn))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
// Return the next code that doesn't exist
return rdr.HasRows ? regenerateCode(helper) : helper;
}
}
}
}
Also, remember that while string are reference types, they are immutable. which means that a string that is passed as an argument would not change like a normal array. see: How are strings passed in .NET?
Related
I have a C# 4.0 WinForms App using SQL Server 2012 database.
On one of my forms, I select a range of dates from a MonthCalendar.
In a query using a SqlDataAdapter, the query should return 4-names of people from a table.
After filling the DataTable, the "for" loop successfully pulls the first name.
On the next iteration, it also pulls the 2nd person's name from the table.
However, on the 3rd iteration, it again pulls the 2nd person's name and does not retrieve the remaining 2-names.
Using SSMS, I can see all 4-names of the people I'm querying. If I use the query below in SSMS, I again get all 4-names.
Does anyone have an idea why the code below fails to return all 4-names, but returns a previous name?
Here is the code I'm using to query the SQL Server database.
private string ReturnPerson(string dStart, string dEnd)
{
string myPerson = "";
try
{
using (SqlConnection conn = new SqlConnection(#"..."))
{
conn.Open();
using (SqlDataAdapter adap = new SqlDataAdapter("SELECT person, scheduledDate FROM Assignments WHERE scheduledDate BETWEEN #start AND #end ORDER BY scheduledDate ASC", conn))
{
adap.SelectCommand.Parameters.Add("#start", SqlDbType.NVarChar).Value = dStart;
adap.SelectCommand.Parameters.Add("#end", SqlDbType.NVarChar).Value = dEnd;
using (DataTable dt = new DataTable())
{
adap.Fill(dt);
for (int i = 0; i < dt.Rows.Count - 1; i++)
{
DataRow row = dt.Rows[i];
DataRow nextRow = dt.Rows[i + 1];
if (personRowCounter == 0)//rowCounter declared globaly
{
myPerson = row.Field<string>("person").ToString();
personRowCounter++;
return myPerson;
}
else if (personRowCounter > 0)
{
myPerson = nextRow.Field<string>("person").ToString();
personRowCounter++;
return myPerson;
}
}
}
}
}
}
catch (SqlException ex) { MessageBox.Show(ex.Message); }
catch (System.Exception ex) { MessageBox.Show(ex.Message); }
return myPerson;
}
If you start with personRowCounter =0
Then the first call will return row 0 as personRowCounter = 0 and i = 0 and it will set personRowCounter = 1
Then the next call will return row 1 as personRowCounter > 0 and i= 0 and it will set personRowCounter = 1
And all calls after that will return row 1 as personRowCounter > 0 and the loop always starts from 0
I'm not sure what you're intent is here. But, I believe that the problem lies with
DataRow nextRow = dt.Rows[i + 1];
That will throw an exception when i is pointing to the last row. Because [i + 1] will index beyond the end of dt.rows.
Based on the comments above, I've figured out the solution. I needed to adjust the DataRow rows, as per below. This allowed me to pull in the remaining data.
private string ReturnPerson(string dStart, string dEnd)
{
string myPerson = "";
try
{
using (SqlConnection conn = new SqlConnection(#"..."))
conn.Open();
using (SqlDataAdapter adap = new SqlDataAdapter("SELECT person, scheduledDate FROM Assignments WHERE scheduledDate BETWEEN #start AND #end ORDER BY scheduledDate ASC", conn))
{
adap.SelectCommand.Parameters.Add("#start", SqlDbType.NVarChar).Value = dStart;
adap.SelectCommand.Parameters.Add("#end", SqlDbType.NVarChar).Value = dEnd;
using (DataTable dt = new DataTable())
{
adap.Fill(dt);
for (int i = 0; i < dt.Rows.Count - 1; i++)
{
DataRow row0 = dt.Rows[i];
DataRow row1 = dt.Rows[i + 1];
DataRow row2 = dt.Rows[i + 2];
DataRow row3 = dt.Rows[i + 3];
if (dt.Rows.Count > 4)
{
DataRow row4 = dt.Rows[4];
}
if (personRowCounter == 0)//rowCounter declared globaly
{
myPerson = row0.Field<string>("person").ToString();
personRowCounter++;
return myPerson;
}
else if (personRowCounter == 1)
{
myPerson = row1.Field<string>("person").ToString();
personRowCounter++;
return myPerson;
}
else if (personRowCounter == 2)
{
myPerson = row2.Field<string>("person").ToString();
personRowCounter++;
return myPerson;
"etc.";
}
}
}
}
}
catch (SqlException ex) { MessageBox.Show(ex.Message); }
catch (System.Exception ex) { MessageBox.Show(ex.Message); }
return myPerson;
}
Im trying to update rows in my table using loop, I get no error but nothing changed in my data...Thats my code.. What am I missing ?
private void updateZipInDB(List<ZipObj> zipCodeToUpdate)
{
var comm = "";
string DbConnString = ConfigurationManager.AppSettings["dbConnectionString"].ToString();
using (SqlConnection conn = new SqlConnection(DbConnString))
{
comm = "UPDATE Account SET address1_postalcode = #newVal WHERE AccountId = #accountID";
using (SqlCommand command = new SqlCommand(comm, conn))
{
conn.Open();
command.Parameters.AddWithValue("#newVal", "f");
command.Parameters.AddWithValue("#accountID", "f");
for (int i = 0; i < zipCodeToUpdate.Count; i++)
{
zipCodeToUpdate[i].NewVal + "' WHERE AccountId = '" + zipCodeToUpdate[i].AccountId + "'";
command.Parameters["#newVal"].Value = zipCodeToUpdate[i].NewVal;
command.Parameters["#accountID"].Value = zipCodeToUpdate[i].AccountId;
command.ExecuteNonQuery();
}
conn.Close();
}
}
}
im getting data from the server as expected as you can see here (the data is not null)
but it won't enter to while (reader.Read())
or sometimes after one or two iteration im getting the exception
"Data is Null. This method or property cannot be called on Null values."
again, the data is not null
EDIT: added some code snippet
System.Data.SqlClient.SqlConnection sqlConnection = null;
try
{
sqlConnection = connectToDB();
SqlCommand cmd = new SqlCommand(#"SELECT TOP 50 R.[RoundID] ,[DailyOrder] ,min(R.[RoundName]) as RoundName, min([EquipCode]) as TruckCode,
sum(RD.[Weight]) as [Weight], RD.BlilCode, min(Blil.BlilName) as BlilName
FROM [CVfeedDB].[dbo].[Planning.Rounds] as R
left join [CVfeedDB].[dbo].[Planning.RoundsDetail] as RD on R.RoundID = RD.RoundID
left join [CVfeedDB].[dbo].[constants.Blil] as Blil on RD.BlilCode = Blil.BlilCode
WHERE R.[ActionDate] = #ActionDate
Group by R.[RoundID] ,[DailyOrder], RD.BlilCode
order by [DailyOrder] ", sqlConnection);
cmd.CommandType = System.Data.CommandType.Text;
//string date = Convert.ToString();
var dt = DateTime.ParseExact(Variables().Item("Date Select").get_Value(0).ToString(), "dd/MM/yyyy", null);
var dt1 = dt.Date.ToString("yyyy-MM-dd");
cmd.Parameters.AddWithValue("#ActionDate",dt1 );
string prefix = "";
int i = 1;
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
prefix = ordersName + i.ToString() + "]";
List<object> list = new List<object>();
List<object> IDList = new List<object>();
int id = Convert.ToInt32(Variables().Item(prefix + ".LoopID").get_Value(0));
int k = reader.GetInt32(0);
if (id == 0 || id == k)//if no id or we are writing the same id
{
Variables().Item(prefix + ".LoopID").set_Value(0, reader.GetInt32(0));
//Variables().Item(prefix + ".").set_Value(0, reader.GetInt32(1)); //Order sequence
Variables().Item(prefix + ".LoopName").set_Value(0, reader.GetString(2));
Variables().Item(prefix + ".Truck").set_Value(0, Convert.ToInt32(reader.GetString(3)));
Variables().Item(prefix + ".Weight").set_Value(0, Convert.ToInt32(reader.GetDecimal(4)));
Variables().Item(prefix + ".MixID").set_Value(0, Convert.ToInt32(reader.GetString(5)));
Variables().Item(prefix + ".MixName").set_Value(0, reader.GetString(6));
}
please not that Variables().Item(...) it how I communicate with 3rd party software
First problem is that your SqlConnection is null. It will always return null value.
Secondally
You should check the reader has rows like i m using reader.Hasrows
System.Data.SqlClient.SqlConnection sqlConnection = null;
try
{
sqlConnection = connectToDB();
SqlCommand cmd = new SqlCommand(#"SELECT TOP 50 R.[RoundID] ,[DailyOrder] ,min(R.[RoundName]) as RoundName, min([EquipCode]) as TruckCode,
sum(RD.[Weight]) as [Weight], RD.BlilCode, min(Blil.BlilName) as BlilName
FROM [CVfeedDB].[dbo].[Planning.Rounds] as R
left join [CVfeedDB].[dbo].[Planning.RoundsDetail] as RD on R.RoundID = RD.RoundID
left join [CVfeedDB].[dbo].[constants.Blil] as Blil on RD.BlilCode = Blil.BlilCode
WHERE R.[ActionDate] = #ActionDate
Group by R.[RoundID] ,[DailyOrder], RD.BlilCode
order by [DailyOrder] ", sqlConnection);
cmd.CommandType = System.Data.CommandType.Text;
//string date = Convert.ToString();
var dt = DateTime.ParseExact(Variables().Item("Date Select").get_Value(0).ToString(), "dd/MM/yyyy", null);
var dt1 = dt.Date.ToString("yyyy-MM-dd");
cmd.Parameters.AddWithValue("#ActionDate",dt1 );
string prefix = "";
int i = 1;
SqlDataReader reader = cmd.ExecuteReader();
if(reader.HasRows)
{
while (reader.Read())
{
prefix = ordersName + i.ToString() + "]";
List<object> list = new List<object>();
List<object> IDList = new List<object>();
int id = Convert.ToInt32(Variables().Item(prefix + ".LoopID").get_Value(0));
int k = reader.GetInt32(0);
if (id == 0 || id == k)//if no id or we are writing the same id
{
Variables().Item(prefix + ".LoopID").set_Value(0, reader.GetInt32(0));
//Variables().Item(prefix + ".").set_Value(0, reader.GetInt32(1)); //Order sequence
Variables().Item(prefix + ".LoopName").set_Value(0, reader.GetString(2));
Variables().Item(prefix + ".Truck").set_Value(0, Convert.ToInt32(reader.GetString(3)));
Variables().Item(prefix + ".Weight").set_Value(0, Convert.ToInt32(reader.GetDecimal(4)));
Variables().Item(prefix + ".MixID").set_Value(0, Convert.ToInt32(reader.GetString(5)));
Variables().Item(prefix + ".MixName").set_Value(0, reader.GetString(6));
}
}
}
try this.
string namethestore = myReader.IsDBNull(namePos)
? string.Empty
: reader.GetString(reader.GetString(2));
Variables().Item(prefix + ".LoopName").set_Value(0, namethestore);
I have added isNull(rowname, 0 ) to the problematic rows and it solved my problem
My function: Removes the unit in a specific row of a given Table
private static void removeUnits(String connectionString, String tableName, String columnID, String columnToFix)
{
List<String> rowsToEdit = new List<String>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT " + columnID + "," + columnToFix + " FROM " + tableName;
connection.Open();
using (var reader = command.ExecuteReader())
{
var indexOfR_MEASUREDVALUEID = reader.GetOrdinal(columnID);
var indexOfT_VALUE = reader.GetOrdinal(columnToFix);
while (reader.Read())
{
var t_value = reader.GetValue(indexOfT_VALUE);
var t_id = reader.GetValue(indexOfR_MEASUREDVALUEID);
String newValue = getWithoutUnit(t_value.ToString());
if (newValue != null)
{
String sql = "UPDATE " + tableName + " SET " + columnToFix + "='" +
newValue + "' WHERE " + columnID + "='" + t_id + "';";
rowsToEdit.Add(sql);
}
}
connection.Close();
}
}
Console.WriteLine("start writing " + rowsToEdit.Count + " entries?");
Console.ReadLine();
SqlCommand sqlCmd;
sqlCmd = new SqlCommand("", connection);
sqlCmd.Connection.Open();
foreach (String command in rowsToEdit)
{
sqlCmd.CommandText = command;
sqlCmd.ExecuteNonQuery();
}
}
Console.WriteLine(rowsToEdit.Count + " commands executed");
}
I am using C# in Visual Studio 2010 and SQL-Server 2012.
It works fine, but executing of 200000 lines takes really long.
Is its possible to do this faster?
My suggestion involves looking into parallelism:
Note: The code isn't tested, just typed it out in Notepad++
private static void removeUnits(String connectionString, String tableName, String columnID, String columnToFix)
{
List<new Tuple<object, object>> rowsToEdit = new List<new Tuple<object, object>>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT " + columnID + "," + columnToFix + " FROM " + tableName;
connection.Open();
using (var reader = command.ExecuteReader())
{
var indexOfR_MEASUREDVALUEID = reader.GetOrdinal(columnID);
var indexOfT_VALUE = reader.GetOrdinal(columnToFix);
while (reader.Read())
{
rowsToEdit.Add(new Tuple<object, object>(reader.GetValue(indexOfT_VALUE),reader.GetValue(indexOfR_MEASUREDVALUEID)));
}
connection.Close();
}
}
}
// Use parallelism here
Parallel.Foreach(rowsToEdit, currentRow =>
{
String newValue = getWithoutUnit(currentRow.Value1.ToString());
if (newValue != null)
{
// reopen connection
// use parameters here, and call SP
}
}
Console.WriteLine("start writing " + rowsToEdit.Count + " entries?");
Console.ReadLine();
}
Stored Proc:
Create StoredProcedure MySP
(
#tablename varchar(50),
#columnToFix varchar(50),
#newValue varchar(50),
#columnID varchar(50),
#tID varhcar(varchar(50)
)
As
Begin
Declare #MySQL varchar(500)
Set #MySQL = 'Update ' + #tableName + ' Set ' + #columnToFix + ' = '' + #newValue + ''' + ' Where ' + #columnID + ' = '' + #tID + '''
sp_executesql #MySQL
End
It's important to note that dynamic sql is generally frowned down upon. Here's a link discussing dynamic sql (pros/cons):http://www.sommarskog.se/dynamic_sql.html
Also it's possible you may run into some locking/contention issues b/c you're hitting one table(based on the input parameters of the function).
First try to use Parameters as already mentioned in the comments. Second, use the SqlCommand.Prepare() statement. The actual speed also depends on your machine capacities (HD Write Speed/RAM). Here is the example (you may have to edit the SqlDbTypes according to your Colum Types)
private static void RemoveUnits(string connectionString, string tableName, string columnID, string columnToFix)
{
Dictionary<int, string> rowsToEdit = new Dictionary<int, string>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT " + columnID + "," + columnToFix + " FROM " + tableName;
using (var reader = command.ExecuteReader())
{
var indexOfR_MEASUREDVALUEID = reader.GetOrdinal(columnID);
var indexOfT_VALUE = reader.GetOrdinal(columnToFix);
while (reader.Read())
{
string t_value = reader.GetString(indexOfT_VALUE);
int t_id = reader.GetInt32(indexOfR_MEASUREDVALUEID);
string newValue = getWithoutUnit(t_value);
if (newValue != null)
{
// save values in dictionary
rowsToEdit[t_id] = newValue;
}
}
}
}
Console.WriteLine("start writing " + rowsToEdit.Count + " entries?");
Console.ReadLine();
string updateCmd = "UPDATE " + tableName + " SET " + columnToFix + "= #Value WHERE " + columnID + "= #Key;";
using (SqlTransaction transaction = connection.BeginTransaction())
{
SqlCommand sqlCmd = connection.CreateCommand();
sqlCmd.CommandText = updateCmd;
sqlCmd.Parameters.Add("#Value", System.Data.SqlDbType.NVarChar, -1);
sqlCmd.Parameters.Add("#Key", System.Data.SqlDbType.Int);
// important for performance
sqlCmd.Prepare();
foreach (var update in rowsToEdit)
{
// change values of parameters
sqlCmd.Parameters["#Key"].Value = update.Key;
sqlCmd.Parameters["#Value"].Value = update.Value;
// and execute it
sqlCmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
Console.WriteLine(rowsToEdit.Count + " commands executed");
}
Edit: if getWithoutUnit(string) does something not very complex, you could probably do your operation with a single statement using TSQL, which comes with various string manipulation capabilities. Like UPDATE TableName SET StringColumn = <string manipulation TSQL here>, see http://msdn.microsoft.com/en-us/library/ms181984.aspx
Edit2: added transaction from comment hint (indeed should be faster as well), strongly typed columns
I have some links on a page such as:
< a id="Digg" runat="server">< img alt="Digg" id="imgDigg" runat="server" src="~/resources/images/icons/social/digg_32.png" border="0" />< /a>
I have a database table that can "turn" them on or off, in my code behind I have the following:
string[] SocialMedia = new string[] { "Twitter", "Facebook", "LinkedIn", "Digg", "Email", "Print" };
private void CheckForSocialMedia()
{
int i = 0;
for (i = 0; i < SocialMedia.Length; i++)
{
bool AddSocialMedia = (bool)AllowSocialMedia(SocialMedia[i]);
if (AddSocialMedia == false)
{
// Hide the link: Digg.Visible = false;
}
}
}
protected bool AllowSocialMedia(string sSocialMedia)
{
SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConn"].ConnectionString);
SqlCommand myCommand = myConnection.CreateCommand();
myCommand.CommandText = " select Settings.AllowsocialMedia " +
" from ArticleSocialMediaSettings Settings " +
" inner join SocialMedia on Settings.SocialMediaId = SocialMedia.id " +
" where SocialMedia.NetworkName = '" + sSocialMedia + "'" +
" and Settings.RetailerId = '1234'";
myConnection.Open();
try
{
SqlDataReader myReader = myCommand.ExecuteReader();
if (myReader.Read())
{
return (bool)myReader["AllowsocialMedia"];
}
}
catch (Exception ex)
{
throw ex;
}
return true;
}
How can I hide the link if the function returns false? Or is there a better way to do this?
It looks like that is the correct method for checking the database. To hide the link you can use:
document.getElementById("Digg").style.display = "none";
right where your comment is.
Redesigned it using a datalist