I have a WebService that updates my access table from some terminals (10).
When I try to update I get this error from the error log:
Could not Update; Currently locked
Some terminals succeed and some do not.
I update like this:
using (Conn = new OleDbConnection(Work_Connect))
{
Conn.Open();
foreach (DataRow R in ds.Tables["MyCount"].Rows)
{
U_ID = ID;
U_Bar = R["Bar"].ToString().Trim();
U_Qty = R["Qty"].ToString().Trim();
U_Des = R["Des"].ToString().Trim();
SQL = "INSERT INTO MyTbl(ID,Bar,Qty,Des)VALUES('";
SQL += Convert.ToInt32(ID) + "','" + U_Bar + "','" + Convert.ToDouble(U_Qty) + "','" + U_Des + "')";
OleDbCommand Cmd2 = new OleDbCommand(SQL, Conn);
Cmd2.CommandText = SQL;
Cmd2.ExecuteNonQuery();
}
}
GC.Collect();
return true;
MsAccess has serious drawbacks for multi-user update. The Jet engine is not a database server, and will manage concurrence based on file system locking. If your problem is with a web service, I'd move the update to the server part, and implement queuing of simultaneous requests there. Thus, only the server, one process, will have access to the Access data. The other option is to use a real database server that will do that work for you. SQL Server Express is the usual option because it's easy to integrate, it's free as in beer, and is solid.
Also, if your problem happens always from the same terminals, that is, some terminals can never update anything, check the file access rights of these terminals' users to the database file, the lock file, and the database and lock file directory. Write rights are required for all of them.
Suggestions:
Convert your query to a parameterized query to avoid any potential strangeness with quoting. (You are converting text to numbers and then enclosing them in single-quotes in the SQL statement. That makes no sense.)
Don't force garbage collection on each call. According to the MSDN article here: "It is possible to force garbage collection by calling Collect, but most of the time, this should be avoided because it may create performance issues."
Try something like this instead:
using (Conn = new OleDbConnection(Work_Connect))
{
Conn.Open();
foreach (DataRow R in ds.Tables["MyCount"].Rows)
{
U_ID = ID;
U_Bar = R["Bar"].ToString().Trim();
U_Qty = R["Qty"].ToString().Trim();
U_Des = R["Des"].ToString().Trim();
SQL = "INSERT INTO MyTbl (ID,Bar,Qty,Des) VALUES (?,?,?,?)";
using(OleDbCommand Cmd2 = new OleDbCommand(SQL, Conn))
{
// Cmd2.CommandText = SQL; redundant, the 'new' set the .CommandText
Cmd2.Parameters.AddWithValue("?", Convert.ToInt32(ID));
Cmd2.Parameters.AddWithValue("?", U_Bar);
Cmd2.Parameters.AddWithValue("?", Convert.ToDouble(U_Qty));
Cmd2.Parameters.AddWithValue("?", U_Des);
Cmd2.ExecuteNonQuery();
}
}
Conn.Close();
}
// GC.Collect(); // disabled for test purposes
return true;
Related
I am developing an asp.net web application and I am trying to add a user xp system to it. I have a SQL Server database connected to it and I am trying to make a function that will give 5 experience points to the user.
I queried to the user that is logged in, accessed the user_xp column, and I am trying to add +5 to the old session variable for xp, then send that back into the database to be stored. Here is my code, I am not sure what is wrong with it.
void generateXp()
{
try
{
SqlConnection con = new SqlConnection(strcon);
if (con.State == ConnectionState.Closed)
{
con.Open();
}
SqlCommand cmd = new SqlCommand("UPDATE member_master_tbl SET user_xp = #user_xp WHERE " +
"user_name = '" + Session["username"].ToString().Trim() + "'", con);
int xp = 5;
int current_xp = Convert.ToInt32(Session["user_xp"]);
int new_xp = xp + current_xp;
string new_xp2 = Convert.ToString(new_xp);
cmd.Parameters.AddWithValue("user_xp", new_xp2);
}
catch (Exception ex)
{
}
}
Try renaming the SQL parameter to #user_xp.
cmd.Parameters.AddWithValue("#user_xp", new_xp2);
I don't have an accessible database to test. Also, you need to add the command to execute the query at the end.
cmd.ExecuteNonQuery()
That being said, it's a good practice to learn to separate DB queries to stored procedures or functions.
As others noted, you simply forgot to do a execute non query to run the command that you setup.
However, you can write things this way. You don't mention or note what the data type the experience points column is - I assumed "int".
So, your code block can be written this way:
using (SqlCommand cmd = new SqlCommand("UPDATE member_master_tbl SET user_xp = #user_xp WHERE user_name = #user",
new SqlConnection(strcon)))
{
cmd.Parameters.Add("#user_xp", SqlDbType.Int).Value = 5 + Session("user_xp");
cmd.Parameters.Add("#user", SqlDbType.NVarChar).Value = Session("username");
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
note how the command object has a connection object (so we don't need a separate one).
And while several people here "lamented" the string concentration to build the sql and warned about sql injection?
Actually, the introduction of # parameters for both values cleans up the code. So you get nice parameters - nice type checking, and you don't have to remember to add/use/have things like quotes around teh string, but not for numbers.
And I let .net cast the number expression from session() - this also likely is ok.
Also the "using block" also correctly cleans up the command object and also the connection object - so the using block is a good idea here.
string query = "SELECT * FROM staff";
string mySQLConnectionString = "datasource=127.0.0.1;port=3306;username=root;password=;database=workshopdb;sslmode=none";
MySqlConnection databaseConnection = new
MySqlConnection(mySQLConnectionString);
MySqlCommand commandDatabase = new MySqlCommand(query, databaseConnection);
databaseConnection.Open();
MySqlDataReader myReader = commandDatabase.ExecuteReader();
if (myReader.HasRows) //checks whether the table is empty
{
while (myReader.Read()) //reads rows consequently
{
MessageBox.Show(myReader.GetString(0) + " " + myReader.GetString(1) + " " + myReader.GetString(3));
//get strings(x) are columns of the table in the db
}
}
databaseConnection.Close();
}
I used this code but It doesn't recognize the username and password that I entered. Instead of recognizing the entered user it shows all users in the database.
In C#, building platform is .NET. Most of the time we can use MSSQL for DB activities. To Configure we can use MSSQL Server Express. XAMPP runs on Apache server. But for the .NET development we need IIS server. At your end arise conflicts. Do more research abourt what are you doing and get know about the dependent technologies
As I can read from SQLite FAQ it supports multiple processes reading (SELECT) and only one process writing (INSERT, UPDATE, DELETE) database at any moment in time:
SQLite uses reader/writer locks to control access to the database.
When any process wants to write, it must lock the entire database file
for the duration of its update. But that normally only takes a few
milliseconds. Other processes just wait on the writer to finish then
continue about their business
I'm using System.Data.SQLite adapter via c#.
Could someone expalin me plz, how exactly this process is going on?
Will this process work automatically and writing SQLiteCommand will simply wait if there is another writing SQLiteCommand already executing over the same database?
Or maybe it will throw an exception? What kind of it?
Sorry but I found no information about this mechanics :)
Thank you.
UPDATE:
I've found post saying that exception will be raised with a specific errorcode
Is that statement correct?
I've investigated it by myself:
I created a sample SQLite database c:\123.db with one table Categories containing two fields: ID (uniqueidentifier) and Name (nvarchar).
I then wrote some multi-thread code to emulate multiple write access to the database (don't forget to add a System.Data.SQLite reference to your project if you use this code):
using System;
using System.Data.SQLite;
using System.Threading.Tasks;
namespace SQLiteTest
{
class Program
{
static void Main(string[] args)
{
var tasks = new Task[100];
for (int i = 0; i < 100; i++)
{
tasks[i] = new Task(new Program().WriteToDB);
tasks[i].Start();
}
foreach (var task in tasks)
task.Wait();
}
public void WriteToDB()
{
try
{
using (SQLiteConnection myconnection = new SQLiteConnection(#"Data Source=c:\123.db"))
{
myconnection.Open();
using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
{
using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
{
Guid id = Guid.NewGuid();
mycommand.CommandText = "INSERT INTO Categories(ID, Name) VALUES ('" + id.ToString() + "', '111')";
mycommand.ExecuteNonQuery();
mycommand.CommandText = "UPDATE Categories SET Name='222' WHERE ID='" + id.ToString() + "'";
mycommand.ExecuteNonQuery();
mycommand.CommandText = "DELETE FROM Categories WHERE ID='" + id.ToString() + "'";
mycommand.ExecuteNonQuery();
}
mytransaction.Commit();
}
}
}
catch (SQLiteException ex)
{
if (ex.ReturnCode == SQLiteErrorCode.Busy)
Console.WriteLine("Database is locked by another process!");
}
}
}
}
The result on my Core2Duo E7500 is that Exception is never raised!
Looks like SQLite is optimised enough for my needs (locking/unlocking is really fast and normally only takes a few milliseconds as SQLite FAQ tells us) - Great!
Note that there is no need to retrieve an integer ErrorCode for an SQLiteException - you can use a special enum ReturnCode field instead. All codes are described here.
Hope this information will help somebody.
I have a problem with a customer. I have this code:
var conn = new SqlConnection(Util.GetConnectionString());
var DataCommand = new SqlCommand();
var sql = "";
// subseccion
try
{
sql = "TRUNCATE TABLE preview_" + tablename;
DataCommand = new SqlCommand(sql, conn);
DataCommand.Connection.Open();
int numcol = DataCommand.ExecuteNonQuery();
sql = "insert into preview_" + tablename+ " select * from " + tablename;
DataCommand = new SqlCommand(sql, conn);
DataCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
var latest_error = ex.Message;
Util.Add_Event_Log(latest_error);
}
finally
{
DataCommand.Dispose();
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Dispose();
}
This do the next thing, I give a name of a table, it TRUNCATE a table then copy the information from "table" to "preview_table" and it works as expected.
However, we found that if we don't give TRUNCATE permission for the table, it fail. But, my problem is that it does not only fail but also deleting the current session (and may be also restart the server process).
My bet it is a server problem (server 2003) may be it is not patched or anything because I am working inside a try-catch part so it should not fail in this fashion.
My customers says the problem is in the code.
But I am not sure, maybe I should not a sql command in a chain.
Is this happening in the development environment as well as production environment? If so, you need to step through your code with the VS debugger and pin point the line at which the session is being deleted.
You should also check the event logs on the production server to see if they can provide any information.
As stated in the comments by msergey, it may be the Util.Add_Event_Log throwing an exception but you should test this by stepping through the code.
If it is Util.Add_Event_Log causing the issue, move this code out of the catch into its own try/catch statement by declaring an exception variable in the outer scope.
If it does wind up that the use of TRUNCATE is the culprit you might try swapping that out in favor of using a DELETE statement instead. Performance won't be as great, but you wouldn't require elevated user permissions in SQL Server either.
I am using the following code do get all data records from a MS SQL database and I try to update every single record. The code is used in a WebService. The issue is, that the code runs fine if I have 1000 data records but now I have 20000 data records an the code first returned with an timeout. Then I set the cmd.CommandTimeout to zero to have no timeout. Now when I invoke the function in the IE WebSvc the IE window is still blank and still try to load something but nothing happens. Only 150 datarecords are updated.
Do you have any idea where the issue might be ? Is the code not the best, so what should I change ?
Thank you very much!
WorldSignia
MyCode:
private string AddNewOrgBez()
{
try
{
SqlConnection sqlconn = new SqlConnection(this.connectionString);
SqlCommand cmd;
SqlDataReader reader;
sqlconn.Open();
cmd = new SqlCommand("SELECT * FROM dbo.mydata", sqlconn);
cmd.CommandTimeout = 0;
reader = cmd.ExecuteReader();
while (reader.Read())
{
// Felder holen
string okuerzel = reader["O_KURZ"].ToString();
string bezeichnung = reader["O_BEZ"].ToString();
string[] lines = CreateNewOrgBez(bezeichnung);
string sqlcmd = "UPDATE dbo.mydata SET WEB_OBEZ1 = '" + lines[0] + "', WEB_OBEZ2 = '" + lines[1] + "', WEB_OBEZ3 = '" + lines[2] + "' WHERE O_KURZ = '" + okuerzel + "'";
SqlConnection sqlconn2 = new SqlConnection(this.connectionString);
sqlconn2.Open();
SqlCommand cmd2 = new SqlCommand(sqlcmd, sqlconn2);
cmd2.CommandTimeout = 0;
cmd2.ExecuteNonQuery();
sqlconn2.Close();
}
reader.Close();
sqlconn.Close();
return "OK";
}
catch (Exception ex)
{
return ex.Message;
}
}
You are leaking every SqlCommand here - I suggest you review your use of SqlClient classes to find the ones that are IDisposable and restructure your code to ensure they are always freed, using the using construct.
For example, this ensures Dispose gets called even if there is an exception in the bracketed code:
using (SqlCommand cmd2 = new SqlCommand(sqlcmd, sqlconn2))
{
cmd2.CommandTimeout = 0;
cmd2.ExecuteNonQuery();
}
Using a new SqlConnection for every UPDATE is expensive too, this should be done outside the loop. Redundant connection establishment is likely the explanation for your timeout.
Take note of #ck's comment that for efficiency this type of piecemeal client-side operation is not as good as doing the heavy lifting server-side. You should be able to get this code working better, but that does not mean it's the ideal/fastest solution.
I found the issue.
You need first to get all the data records, for example in a new DataTable. The structure I used does not work, because it reads data from the database and also updates the database. After changing it to a new structure it works.
You were using two different connections to read and to update, and one of them was blocking another. This is why when you read all your data first, it began to work.
I doubt if your running into OutOfMemoryException. Can you profile your application and check the memory usage?
Since you are just overwriting the variables in While loop, why don't you try taking them out of the loop.