I use OleDbDataAdapter and OleDbCommandBuilder to fill DataSet object with database contents, and then update database according to a changes that I made in the DataSet. The problem is that I get the exception: "Concurrency violation: the UpdateCommand affected 0 of the expected 1 records". I've found an explanation of this error:
Because a record could have been modified after it was returned from
the SELECT statement, but before the UPDATE or DELETE statement is
issued, the automatically generated UPDATE or DELETE statement
contains a WHERE clause, specifying that a row is only updated if it
contains all original values and has not been deleted from the data
source. Where an automatically generated update attempts to update a
row that has been deleted or that does not contain the original values
found in the DataSet, the command does not affect any records, and a
DBConcurrencyException is thrown.
That means that auto generated UPDATE command affected 0 rows in the database. I work with paradox(db-file) database and no one changes it except for me. I guess that my program changes the same row two times somewhere. I wanted to debug my program by executing all generated queries manually and finding which one doesn't affect any row(because actually I'm pretty sure that all changes are made only once and the bug is somewhere else))). Is it possible to run auto generated commands manually?
My code is too big and complicated to post it here but generally it works like this(I made a working project and took it from there)
using System;
using System.Data;
using System.Windows.Forms;
using System.Data.OleDb;
namespace OleDBCommandBuilder
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string cs = #"Provider=Microsoft.Jet.OLEDB.4.0;";
cs += #"Data Source=C:\FOLDER\1\SPR_KMZ\;";
cs += #"Extended Properties=Paradox 5.x;";
OleDbConnection Connection = new OleDbConnection();
Connection.ConnectionString = cs;
try
{ Connection.Open(); }
catch (Exception ex)
{ MessageBox.Show("Error openning database! " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); }
string SQLQuery = "SELECT * FROM SPR_KMZ WHERE REZ<>0";
DataSet SPR_KMZ = new DataSet();
OleDbDataAdapter DataAdapter = new OleDbDataAdapter();
DataAdapter.SelectCommand = new OleDbCommand(SQLQuery, Connection);
OleDbCommandBuilder builder = new OleDbCommandBuilder(DataAdapter);
try
{
DataAdapter.Fill(SPR_KMZ);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(String.Format("Error \n{0}\n{1}", ex.Message, SQLQuery));
Environment.Exit(0);
}
DataRow[] SPR_KMZ_rows = SPR_KMZ.Tables[0].Select("Fkmz=10000912 AND REZ=1");
foreach (DataRow SPR_KMZ_row in SPR_KMZ_rows)
{
SPR_KMZ_row["DN"] = Convert.ToDateTime("30.12.1899");//26.12.2008
SPR_KMZ_row["Price"] = Convert.ToDouble(0);//168,92
}
DataAdapter.Update(SPR_KMZ);
System.Windows.Forms.MessageBox.Show("Success!");
Environment.Exit(0);
}
}
}
P.S. Previously it updated the database without concurrency exception, but after a lot of changes(I commented out the line "DataAdapter.Update(SPR_KMZ);" for a long time for debugging reason, so I don't know when exactly this error started to throw)
P.S.S. there are no INSERTs or DELETEs in my code, only UPDATEs...
<<UPDATE>>
I've found what was the problem: if "DN" field has NULL value then after changing it, the auto-generated UPDATE Statement don't affect anything, obviously because "DN" is contained in a primary key and command builder didn't expect for primary key field to have NULL values(who ever would))), no surprise this engine is called "Paradox")))
that's why in
CommandBuilder.GetUpdateCommand().CommandText
in where clause for "DN" field there was this kind of pattern:
... WHERE ((REZ = ?) AND (DN = ?) AND ...
while nullable fields are described like this:
... AND ((? = 1 AND Price IS NULL) OR (Price = ?)) AND ((? = 1 AND Nmed IS NULL) OR (Nmed = ?)) AND ...
P.S.S.S. Hey, I can try to set UpdateCommand manually to fix this!)))
Here is how I've managed to set the UpdateCommand manually and even get SQL code for every UPDATE command that is being executed!(more or less)). It is very helpful while debugging - I can see what sql query failed to execute during DataAdapter.Update(DBDataSet) command.
public void Update(DataSet DBDataSet)
{
DataAdapter.RowUpdating += before_update;
DataAdapter.Update(DBDataSet);
}
public void before_update(object sender, EventArgs e)
{
//Convert EventArgs to OleDbRowUpdatingEventArgs to be able to use OleDbCommand property
System.Data.OleDb.OleDbRowUpdatingEventArgs oledb_e = (System.Data.OleDb.OleDbRowUpdatingEventArgs) e;
//Get query template
string cmd_txt = oledb_e.Command.CommandText;
//Modify query template here to fix it
//cmd_txt = cmd_txt.Replace("table_name", "\"table_name\"");
//fill tamplate with values
string cmd_txt_filled = cmd_txt;
foreach(System.Data.OleDb.OleDbParameter par in oledb_e.Command.Parameters)
{
string par_type = par.DbType.ToString();
string string_to_replace_with = "";
if (par.Value.GetType().Name == "DBNull")
{
string_to_replace_with = "NULL";
}
else
{
if (par_type == "Int32")
{
par.Size = 4;
string_to_replace_with=Convert.ToInt32(par.Value).ToString();
}
else if (par_type == "Double")
{
par.Size = 8;
string_to_replace_with=Convert.ToDouble(par.Value).ToString().Replace(",",".");
}
else if (par_type == "DateTime")
{
par.Size = 8;
/* In Paradox SQL queries you can't just specify the date as a string,
* it will result in incompatible types, you have to count the days
* between 30.12.1899 and the required date and specify that number
*/
string_to_replace_with = DateToParadoxDays(Convert.ToDateTime(par.Value).ToString("dd.MM.yyyy"));
}
else if (par_type == "String")
{
string_to_replace_with = '"' + Convert.ToString(par.Value) + '"';
}
else
{
//Break execution if the field has a type that is not handled here
System.Diagnostics.Debugger.Break();
}
}
cmd_txt_filled = ReplaceFirst(cmd_txt_filled, "?", string_to_replace_with);
}
cmd_txt_filled = cmd_txt_filled.Replace("= NULL", "IS NULL");
//Get query text here to test it in Database Manager
//System.Diagnostics.Debug.WriteLine(cmd_txt_filled);
//Uncomment this to apply modified query template
//oledb_e.Command.CommandText = cmd_txt;
//Uncomment this to simply run the prepared update command
//oledb_e.Command.CommandText = cmd_txt_filled;
}
public string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
private static string DateToParadoxDays(string date)
{
return (Convert.ToDateTime(date) - Convert.ToDateTime("30.12.1899")).TotalDays.ToString();
}
Related
I try to update a row in my Access, my code is running fine and I have to Exception, But is nothing change un my database
This is my method it calls from a form in a Winform project
public static void UpdateNextReportNumber(int machineNumber, string reportNumber)
{
try
{
using (OleDbConnection openCon = new OleDbConnection(localConnectionString))
{
string saveStaff = "UPDATE [Calibration] " +
"SET [NextReportNumber]=#report " +
"where [MachineNumber]=#machine";
using (OleDbCommand querySaveStaff = new OleDbCommand(saveStaff))
{
querySaveStaff.Connection = openCon;
querySaveStaff.Parameters.AddWithValue("#machine", 16);
querySaveStaff.Parameters.AddWithValue("#report",2);//Convert.ToInt32(reportNumber.Remove(0, 3)) + 1
openCon.Open();
int recordsAffected = querySaveStaff.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
//WriteLog(ex.StackTrace, ex.Message);
throw ex;
}
}
this is how my Calibration table looks like
my code pass this line
int recordsAffected = querySaveStaff.ExecuteNonQuery();
But in recordsAffected I have value 0
I have no idea what to do
I tried to execute using Access this query
UPDATE [Calibration]
SET [NextReportNumber]=2
where [MachineNumber]=36
And its work fine
I also used
public static void AddCalibration(Calibration calibration)
{
try
{
using (OleDbConnection openCon = new OleDbConnection(localConnectionString))
{
string saveStaff = "INSERT into [Calibration] ([MachineNumber] ,[LastCalibrationDate] ,[NextCalibrationDate])" +
"VALUES (#MachineNumber, #LastCalibrationDate, #NextCalibrationDate)";
using (OleDbCommand querySaveStaff = new OleDbCommand(saveStaff))
{
querySaveStaff.Connection = openCon;
querySaveStaff.Parameters.AddWithValue("#MachineNumber", calibration.MachineNumber);
querySaveStaff.Parameters.AddWithValue("#LastCalibrationDate", calibration.LastCalibrationDate);
querySaveStaff.Parameters.AddWithValue("#NextCalibrationDate", calibration.NextCalibrationDate);
openCon.Open();
int recordsAffected = querySaveStaff.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
//WriteLog(ex.StackTrace, ex.Message);
throw ex;
}
}
And it works fine also...
Thanks for help...
In OleDb parameters are not recognized by their name but by their position in the parameters collection. You should simply change the line order of your parameters
querySaveStaff.Parameters.AddWithValue("#report",2);
querySaveStaff.Parameters.AddWithValue("#machine", 16);
In your current query the report's parameter is used in the Where statement not in the update part and of course nothing is updated because there is no record with WHERE MachineNumber = 2
Indeed, in OleDb you usually specify the parameters placeholder with a single ? not with the #something syntax, but Access, probably for easier portability with Sql Server accepts also the # syntax, still the positions in parameter's collection should be the correct one expected in the query text.
When I connect to the DB and I want to read data from table it doesn't work an shows these
Exception Details:
System.IndexOutOfRangeException: Cannot find table 0.
Error in Line 141
I have only one table in the dataset. Do you know the best way to read from table for me ? I return only one table. When I use foreach, how can I read a one row data?
internal DataSet read(string query)
{
DataSet nds = new DataSet();
try
{
string connectionString = "...";
try
{
SqlConnection nsqlc = new SqlConnection(connectionString);
SqlCommand get = new SqlCommand(query, nsqlc);
nsqlc.Open();
SqlDataAdapter nsda = new SqlDataAdapter(get);
nsda.Fill(nds);
nsqlc.Close();
}
catch (Exception)
{
}
return nds;
}
catch (Exception)
{
return nds;
}
}
Main form :
links = links + t.Tables[0].Rows[i][0].ToString() + "%";
String links = "";
links = "movie%";
DataSet t = n.read("select id , poster from Movie order by id desc");
for (int i = 0; i < 10; i++)
{
links = links + t.Tables[0].Rows[i][0].ToString() + "%";
links = links + t.Tables[0].Rows[i][1] + "%";
}
The closest thing to an answer here would have to be that the n.read method returns a DataSet instance that has no tables in it. You need to step into the method with a debugger and figure out why it behaves like that.
If you see what the problem is, fix it. If not, share the read method code and someone will be able to help you further.
Looking at your updated post, I see that you are swallowing every possible exceptions that could occur while trying to fetch data. Remove the catch block from your read method. This will allow you to see what the real problem is.
Exception swallowing is something you'll want to take away from your code on a global scale. I'd suggest that you do some Googling to find out why it's a terrible habit.
About a "better way to read from table", you should consider using a IDataReader.
string links = "movie%";
using (var connection = new SqlConnection("your connection string");
{
using (var command = someExistingConnection.CreateCommand())
{
command.CommandText = "select id, poster from Movie order by id desc";
command.Connection = connection;
connection.Open();
try
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var idValue = reader.GetObject(0).ToString(); // would be better to use actual field type, which is unknown at the time I'm writing this
var posterValue = reader.GetString(1);
// perform concatenation here using the two variables declared above
links += String.Format("{0}%{1}%", idValue, posterValue);
}
}
}
finally
{
connection.Close();
}
}
}
This will outperform working with a DataSet by a long shot.
I am recieving the following error message whenever I click the submit button on my form:
Error: System.ArgumentNullException: Value cannot be null. Parameter name: connectionString at
Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteDataset(String
connectionString, CommandType commandType, String commandText,
SqlParameter[] commandParameters) at
ClassesnWorkshops._Default.GetHousesBySearchCriteria() . . .
I am a beginner with c# and visual studio.
Can anyone tell me why my connection string might be null?
Any ideas how I might resolve this error?
I used the Settings button of the project Properties to create a ConnectionString called connectionString connecting to my Classes database. but this may have been unnecessary, as it did not change the Error.
//Code for my submit button click event:
protected void cmdSearch_Click(object sender, EventArgs e)
{
DataTable objDT = null;
try
{
//Query the database for houses
objDT = GetHousesBySearchCriteria();
//Any houses found?
if (objDT.Rows.Count == 0)
{
//None found - hide repeater, show message
rptHouses.Visible = false;
lblMessage.Text = "No results found";
}
else
{
//Houses found - show repeater, hide message
rptHouses.DataSource = objDT;
rptHouses.DataBind();
rptHouses.Visible = true;
lblMessage.Text = "";
}
}
catch (Exception ex)
{
//Add your own error handling here
lblMessage.Text = "Error: " + ex.ToString();
}
finally
{
//Release memory
objDT = null;
}
}
#endregion
#region GetHousesBySearchCriteria
public DataTable GetHousesBySearchCriteria()
{
//Use the Microsoft Data Application Blocks to query database
DataSet objDS = new DataSet();
SqlParameter[] arParms = new SqlParameter[2];
arParms[0] = new SqlParameter("#Zipcode", SqlDbType.Char);
arParms[0].Value = txtZipCode.Text;
arParms[1] = new SqlParameter("#Miles", SqlDbType.Decimal);
arParms[1].Value = Int16.Parse(cboWithin.SelectedItem.Value);
lblTestParms0.Text = txtZipCode.Text;
lblTestParms1.Text = cboWithin.SelectedValue.ToString();
//Return a DataTable
return SqlHelper.ExecuteDataset(ConfigurationManager.AppSettings["ConnectionString"],
CommandType.StoredProcedure, "spHouses_GetNearZipcode", arParms).Tables[0];
}
#endregion
stored procedure code:
CREATE PROCEDURE [dbo].[spHouses_GetNearZipcode]
#Zipcode char(5),
#Miles decimal(11,6)
AS
--Load close zipcodes into temp table
SELECT ZIP.ZipCode, ZIP.City,
dbo.DistanceFunc(ZIP.Latitude, ZIP.Longitude, RAD.Latitude, RAD.Longitude) As Distance
INTO #TempZips
FROM ZipCodes ZIP, RadiusFunc(#ZipCode, #Miles) RAD
WHERE (ZIP.Latitude BETWEEN RAD.MinLatitude AND RAD.MaxLatitude) AND
(ZIP.Longitude BETWEEN RAD.MinLongitude AND RAD.MaxLongitude) AND
(dbo.DistanceFunc(ZIP.Latitude,ZIP.Longitude,RAD.Latitude,RAD.Longitude) <= #Miles)
--Search Houses table and JOIN to temp zipcodes table
SELECT H.*, Zips.Distance AS Miles
FROM Schools H INNER JOIN
#TempZips Zips ON Zips.ZipCode = H.zip
ORDER BY Zips.Distance
RETURN
I think the ConfigurationManager argument in your ExecuteDataset should look like this:
return SqlHelper.ExecuteDataset(ConfigurationManager.AppSettings["ConnectionString"].ConnectionString, CommandType.StoredProcedure, "spHouses_GetNearZipcode", arParms).Tables[0];
Add ".ConnectionString" after your AppSettings key
When using ConfigurationManager.AppSettings the key values are case sensitive. Is your connection string named
connectionString or ConnectionString
^ ^
My Global Class :
class Global
{
public static OleDbDataAdapter adapter;
public static DataTable dt;
}
The procedure I'm using to fill my DataGridView :
OleDbConnection connection;
OleDbCommandBuilder builder;
void gridfill()
{
connection = new OleDbConnection("Provider=MSDAORA;Data Source=XXX;"
+ "user id=XXX;password=XXX;"
+ "persist security info=false;");
Global.adapter = new OleDbDataAdapter("select \"Id\", \"UserComputer\", \"GuralID\", \"Type\", \"CreatedOn\", \"State\" from COMPUTERS", connection);
builder = new OleDbCommandBuilder(Global.adapter);
Global.dt = new DataTable();
Global.adapter.Fill(Global.dt);
dataGridView1.DataSource = Global.dt;
dataGridView1.ReadOnly = true;
}
The procedure I'm using to update a field in a row in my Oracle DB :
private void button1_Click(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "New")
{
Global.dt.Rows[rowId]["State"] = 0;
}
else if (comboBox1.Text == "Old")
{
Global.dt.Rows[rowId]["State"] = 1;
}
else if (comboBox1.Text == "Junk")
{
Global.dt.Rows[rowId]["State"] = 2;
}
Global.adapter.Update(Global.dt);
this.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
When I run, I get my DataGridView filled. That part is okay. And then I double click on a row and another form shows up. That form gets values of the selected row. There's an update button on it. I use it to change the value of a field of the selected row. 3rd code I shared with you is the one to do that. But I get ORA-00904: "STATE" invalid identifier.
I debugged it. The error comes in this line :
Global.adapter.Update(Global.dt);
TIPS:
Tables are actually created by ORM classes.
As I know this is something about double quotes.
Ex : 'Select State from COMPUTERS' does not work but 'Select "State" from COMPUTERS' does.
I used '\' prefix in my SQL query after having the same issue when filling DataGridView. The problem solved.
But I cannot use it when trying to assign a new value to the field. And I need a way to do that.
I guess the problem is here :
Global.dt.Rows[rowId]["State"] = 0;
What can I do? Thanks.
Try setting the QuotePrefix and QuoteSuffix on your OleDbCommandBuilder object to ".
By default, the OleDbCommandBuilder doesn't know what quotation system the database system it's talking to uses.
This is my second post. After learning from my first post how fantastic is to use Linq to SQL, I wanted to try to import data from a Excel sheet into my SQL database.
First My Excel Sheet:
it contains 4 columns namely
ItemNo
ItemSize
ItemPrice
UnitsSold
I have a created a database table with the following fields
table name ProductsSold
Id int not null identity --with auto increment set to true
ItemNo VarChar(10) not null
ItemSize VarChar(4) not null
ItemPrice Decimal(18,2) not null
UnitsSold int not null
Now I created a dal.dbml file based on my database and I am trying to import the data from excel sheet to db table using the code below.
Everything is happening on click of a button.
private const string forecast_query = "SELECT ItemNo, ItemSize, ItemPrice, UnitsSold FROM [Sheet1$]";
protected void btnUpload_Click(object sender, EventArgs e)
{
var importer = new LinqSqlModelImporter();
if (fileUpload.HasFile)
{
var uploadFile = new UploadFile(fileUpload.FileName);
try
{
fileUpload.SaveAs(uploadFile.SavePath);
if(File.Exists(uploadFile.SavePath))
{
importer.SourceConnectionString = uploadFile.GetOleDbConnectionString();
importer.Import(forecast_query);
gvDisplay.DataBind();
pnDisplay.Visible = true;
}
}
catch (Exception ex)
{
Response.Write(ex.Source.ToString());
lblInfo.Text = ex.Message;
}
finally
{
uploadFile.DeleteFileNoException();
}
}
}
// Now here is the code for LinqSqlModelImporter
public class LinqSqlModelImporter : SqlImporter
{
public override void Import(string query)
{
// importing data using oledb command and inserting into db using LINQ to SQL
using (var context = new WSDALDataContext())
{
using (var myConnection = new OleDbConnection(base.SourceConnectionString))
using (var myCommand = new OleDbCommand(query, myConnection))
{
myConnection.Open();
var myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
context.ProductsSolds.InsertOnSubmit(new ProductsSold()
{
ItemNo = myReader.GetString(0),
ItemSize = myReader.GetString(1),
ItemPrice = myReader.GetDecimal(2),
UnitsSold = myReader.GetInt32(3)
});
}
}
context.SubmitChanges();
}
}
}
can someone please tell me where am I making the error or if I am missing something, but this is driving me nuts.
When I debugged I am getting this error
when casting from a number the value must be a number less than infinity
I really appreciate it
Some options:
Add a watch on myReader.GetValue(0), myReader.GetValue(1),
etc. to see what the source value is. Add a breakpoint on the line
that's throwing the error and see which value is causing the issue.
Change your object initializer to separate calls to see which column is throwing the error:
ProductSold product = new ProductsSold();
product.ItemNo = myReader.GetString(0);
product.ItemSize = myReader.GetString(1);
product.ItemPrice = myReader.GetDecimal(2);
product.UnitsSold = myReader.GetInt32(3);
context.ProductsSolds.InsertOnSubmit(product);