Auto generation of Order id - c#

I wanted to have a unique transaction id for each order placed on my system and it only increments once. this is the code that i am using. help me out to have the incrementation fixed.
string transactionCode;
con = new SqlConnection(#"Data Source=LAPTOP-KA7UGSG3;Initial Catalog=imsysdb;Integrated Security=True");
cmd = new SqlCommand("SELECT TransactionCode from tblOrders", con);
con.Open();
dr = cmd.ExecuteReader();
if (dr.Read())
{
int code = int.Parse(dr[0].ToString()) + 1;
transactionCode = code.ToString("000");
}
else if (Convert.IsDBNull(dr))
{
transactionCode = ("001");
}
else
{
transactionCode = ("001");
}
lblTransactionCode.Text = transactionCode.ToString();
OUTPUT
|transaction code|
|001|
|002|
|002|
|002|

It looks like your current code wants something that you can pull successive values from without inserting rows anywhere. You don't mention what database you are using, but in SQL Server this concept is a "sequence", see CREATE SEQUENCE
However, when talking about things like "orders", the more common approach is to make the Id column an "identity" (see CREATE TABLE / IDENTITY, so that the server generates the Id when you INSERT. You can read the newly generated value with any of (in preference order, earlier is better):
the OUTPUT clause on the INSERT
SCOPE_IDENTITY() (usually fine, but limited to one row)
##IDENTITY (many problems; don't use unless the others aren't available on your server)

I can see that your id generating is not related to SQL, you are trying to generate it in C# for some reason, so try replacing this :
if (dr.Read())
{
int code = int.Parse(dr[0].ToString()) + 1;
transactionCode = code.ToString("000");
}
else if (Convert.IsDBNull(dr))
{
transactionCode = ("001");
}
else
{
transactionCode = ("001");
}
With this:
Edited - code updated
int code; //OR int code = 0;
if (dr.Read())
{
code = int.Parse(dr[0].ToString()) + 1;
transactionCode = code.ToString("000");
}
else if (Convert.IsDBNull(dr))
{
transactionCode = ("001");
}
else
{
transactionCode = ("001");
}
If it does not work, Edit your post and add some sample data, please.

I second the suggestions that you use a SQL identity column. With that said, assuming you don't control schema...
The logic of your attempt here is flawed in a couple of ways...
First I'm pretty sure you want to be querying MAX(TransactionCode)
Second, you can easily have two clients querying very close to each other, they both read the current maximum TransactionCode and both increment it to get the same new code.
Therefore you want to remove the increment from your C# code and do it in SQL script if at all possible with something like this (NB see #Marc Gravell's comment below)...
INSERT INTO tblOrders (TransactionCode, field1, field2...)
VALUES (CONVERT(VARCHAR(10), SELECT CONVERT(INT, MAX(TransactionCode)) + 1) FROM tblOrders, newfield1data, ...)

Related

Value cannot be null c#.net

I am creating a simple project on c#.net. I want to put the AutoNo textbox in my program. I have put but it is not working. it shown the error while ran program error said that
Value cannot be null Parameter name: Stringmscorlib
Code what I tried I attached below
public void Load()
{
SqlConnection Con = new SqlConnection("Data Source=.;Initial Catalog=test1;Persist Security Info=True;User ID=sa;Password=admin123");
Con.Open();
cmd = new SqlCommand("select id from records", Con);
Data = cmd.ExecuteReader();
while (Data.Read() != false)
{
auto = int.Parse(Data[0].ToString());
}
try
{
int newid = auto;
int id = newid + 1;
this.textBox1.Text = "S00" + id.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ex.Source);
}
Data.Close();
}
}
There are two problems I can see here; the first would be: what if there are zero rows? what is auto then? is it null? then int.Parse(null) will fail. You don't actually show where auto is declared, which makes this bit a little hard to intuit about.
The other possibility is here:
auto = Data.GetString(0);
in which case: this is simply a null database value. Check for that, and handle it:
if (Data.IsDBNull(0))
{ ... do whatever; perhaps just "continue" }
else
{
auto = Data.IsDBNull(0);
// and process it, etc
}
But frankly, you're making life hard for yourself here; here's the same thing with a tool like Dapper:
using (var conn= new SqlConnection("...whatever..."))
{
// here we're asserting *exactly* zero or one row
string auto = conn.QuerySingleOrDefault<string>("select id from records");
if (auto == null)
{ ... do something else? ... }
else
{
var newid= int.Parse(auto);
}
}
Note: your query could currently return any number of rows; since the code only processes the last value, I suggest that the SQL needs fixing; perhaps MAX, MIN, or TOP 1 with an ORDER BY clause; i.e. something like select MAX(id) as [id] from records. Note, however, that this sounds like a scenario where you should probably have used SCOPE_IDENTITY() in some query that added or inserted the value. And an id should very rarely be a string.
Parse method is not able to handle null value. Assuming auto is variable name.
instead of this
int newid = Int32.Parse(auto);
use something like below
int newid=0;
int.TryParse(auto, out newid);

Application returns data for me but not other users, even when I log in under their username - SQL Server/C#/WPF

Having a little bit of a strange error here that I have never encountered before. I have an application where users can type in a list of accounts in a datagrid and a date range and press a button and it will return the data for these accounts in a datagrid and give them the option to export it to an excel file. This works perfectly for me, logged in under my username and even when I log in under other people's username. The problem is when they try it, they get no data back. No errors, just it doesn't pull any data.
The interesting thing is this is all in the same database as the other information which they access without any problem. The only difference, which I think might be the explanation is I am calling this SQL code directly from the Application whereas everything else is called using stored procedures that sit on the server. The reason for this is I have to concatenate the SQL Query string for each item in the accounts field. Since they are able to enter as many accounts as they want, I cannot use a stored procedure since I don't know how many parameters it will have ultimately(if someone could let me know a method of doing this, I would actually prefer this way for keeping things consistent). Obviously the query string is working properly, as it's pulling data back for me, but the question I have is why is it failing to return data for others? The connection string is an SQL Authentication, so it shouldn't have anything to do with them not having Windows Authentication on the server, plus they are already able to log in to the application and it displays data on their dashboard, which couldn't happen...
Anyone that can point me in the right direction with this I would appreciate it...the only thing I can think of is it is an issue with using an in-code SQL string versus a stored procedure, but this doesn't make any sense since other people do this all the time in applications without issue.
public ICommand GetData
{
get => new RelayCommand(() =>
{
//call the SQL Code to lookup the account numbers
var SQLStr = "SELECT * FROM [Clients].[Data] WHERE (Account_Number = '";
for (var i = 0; i< AccountNums.Count; i++)
{
if (!String.IsNullOrEmpty(AccountNums[i].accNum)) SQLStr += i == 0 ? $"{AccountNums[i].accNum}'" : $" OR Account_Number = '{AccountNums[i].accNum}'";
}
SQLStr += $") AND SUB_QUERY_CREATED_ON BETWEEN '{StartDate.ToString()}' AND '{EndDate.ToString()}'";
_Data = DBMethods.GetSQLData(_Data, new Models.Clients.Data(), SQLStr, new List<string> { "ID" }, true);
ShowResPnl = true; //there are results, toggle the panel visibility bound variable
});
}
public static ObservableCollection<T> GetSQLData<T>(ObservableCollection<T> myCollection, T myClass, String SQLString, List<string> remParams, bool UseSQLQuery) where T : class
{
var conn = new SqlConnection();
try
{
var paramList = GenerateSQLParameters(myClass, remParams);
using (getConnection(conn))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(SQLString, conn))
{
cmd.CommandType = CommandType.Text;
SqlDataReader reader;
reader = cmd.ExecuteReader();
//only execute if the reader has data
if (reader.HasRows)
{
while (reader.Read())
{
var tempModel = Global.GenerateNewInstance(myClass) as T;
Type model = tempModel.GetType();
var prop = model.GetProperties();
PropertyInfo pi;
//set the values for each property in the model
foreach (var p in prop)
{
if (!remParams.Contains(p.Name))
{
pi = tempModel.GetType().GetProperty(p.Name);
if (reader[p.Name] == DBNull.Value)
{
pi.SetValue(tempModel, null);
}
else
{
pi.SetValue(tempModel, reader[p.Name]);
}
}
}
myCollection.Add(tempModel);
}
reader.Close();
cmd.Dispose();
}
}
}
}
catch (Exception ex)
{
ErrorWindow errWin = new ErrorWindow("There was a problem trying to Get the Data with the Query '" + SQLString + "'! Error: " + ex.Message);
errWin.Show();
}
return myCollection;
}
UPDATE: OK I got it working perfectly with help from THIS thread:
How do I split a string so I can access item x?
and more specifically this post:
What about using string and values() statement?
DECLARE #str varchar(max)
SET #str = 'Hello John Smith'
DECLARE #separator varchar(max)
SET #separator = ' '
DECLARE #Splited TABLE(id int IDENTITY(1,1), item varchar(max))
SET #str = REPLACE(#str, #separator, '''),(''')
SET #str = 'SELECT * FROM (VALUES(''' + #str + ''')) AS V(A)'
INSERT INTO #Splited
EXEC(#str)
SELECT * FROM #Splited
I created a stored procedure using this, then did a left join on Account numbers from the Data Table and used a WHERE clause to set the Start and End Dates and exclude items that were NULL(checked one of the columns). Works perfectly and only took about 2 or 3 seconds to return the data. I had another working method as detailed here https://sqlperformance.com/2012/07/t-sql-queries/split-strings#comments using a function which was taking well over a minute to return data for only 4 accounts...obviously was not going to work well enough so I found the method mentioned prior and it works excellently!

How to count all rows in a data table c#

So I am creating a messaging application for a college project and I have a database of Users in Access, I have linked the database correctly and can execute statements but I am struggling with one problem, how to count the number of rows in a data table.
In fact, all I want to do is to count the total number of users and my teacher told me to get the data into a DataTable and count the number of rows. However, no matter how many users I have in the database, it always returns as 2.
int UserCount = 0;
using (OleDbConnection cuConn = new OleDbConnection())
{
cuConn.ConnectionString = #"DATASOURCE";
string statement = "SELECT COUNT(*) FROM Users";
OleDbDataAdapter da = new OleDbDataAdapter(statement, cuConn);
DataTable Results = new DataTable();
da.Fill(Results);
if (Results.Rows.Count > 0)
{
UserCount = int.Parse(Results.Rows[0][0].ToString());
}
}
The above code is a copy of what I was sent by my teacher who said it would work. Any help would be appreciated.
Also, sorry if this is a waste of time, still getting used to this StackOverflow thing...
Try replace Users with [Users]?
Because Users may be a key word of database.
Also the simpler way to get aggregate numbers is by ExecuteScalar method.
using (OleDbConnection cuConn = new OleDbConnection())
{
cuConn.ConnectionString = #"DATASOURCE";
string statement = "SELECT COUNT(*) FROM [Users]";
OleDbCommand cmd = new OleDbCommand (statement, cuConn);
cuConn.Open();
int count = (int)cmd.ExecuteScalar();
if (count > 0)
{
//
}
}
I successfully used your exact code (except the connection string) with sql server so maybe there is a problem with your #"DATASOURCE" or MS Access.

Check if table exists in c#

I want to read data from a table whose name is supplied by a user. So before actually starting to read data, I want to check if the database exists or not.
I have seen several pieces of code on the NET which claim to do this. However, they all seem to be work only for SQL server, or for mysql, or some other implementation. Is there not a generic way to do this?
(I am already seperately checking if I can connect to the supplied database, so I'm fairly certain that a connection can be opened to the database.)
You cannot do this in a cross-database way. Generally DDL (that is, the code for creating tables, indexes and so on) is completely different from database to database and so the logic for checking whether tables exist is also different.
I would say the simplest answer, though, would simply be something like:
SELECT * FROM <table> WHERE 1 = 0
If that query gives an error, then the table doesn't exist. If it works (though it'll return 0 rows) then the table exists.
Be very careful with what you let the user input, though. What's to stop him from from specifying "sysusers" as the table name (in SQL Server, that'll be the list of all database users)
You can use the DbConnection.GetSchema family of methods to retreive metadata about the database. It will return a DataTable with schema objects. The exact object types and restriction values may vary from vendor to vendor, but I'm sure you can set up your check for a specific table in a way that will work in most databases.
Here's an example of using GetSchema that will print the name and owner of every table that is owned by "schema name" and called "table name". This is tested against oracle.
static void Main(string[] args)
{
string providerName = #"System.Data.OracleClient";
string connectionString = #"...";
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
using (DbConnection connection = factory.CreateConnection())
{
connection.ConnectionString = connectionString;
connection.Open();
DataTable schemaDataTable = connection.GetSchema("Tables", new string[] { "schema name", "table name" });
foreach (DataColumn column in schemaDataTable.Columns)
{
Console.Write(column.ColumnName + "\t");
}
Console.WriteLine();
foreach (DataRow row in schemaDataTable.Rows)
{
foreach (object value in row.ItemArray)
{
Console.Write(value.ToString() + "\t");
}
Console.WriteLine();
}
}
}
That's like asking "is there a generic way to get related data" in databases. The answer is of course no - the only "generic way" is to have a data layer that hides the implementation details of your particular data source and queries it appropriately.
If you are really supporting and accessing many different types of databases without a Stategy design pattern or similar approach I would be quite surprised.
That being said, the best approach is something like this bit of code:
bool exists;
try
{
// ANSI SQL way. Works in PostgreSQL, MSSQL, MySQL.
var cmd = new OdbcCommand(
"select case when exists((select * from information_schema.tables where table_name = '" + tableName + "')) then 1 else 0 end");
exists = (int)cmd.ExecuteScalar() == 1;
}
catch
{
try
{
// Other RDBMS. Graceful degradation
exists = true;
var cmdOthers = new OdbcCommand("select 1 from " + tableName + " where 1 = 0");
cmdOthers.ExecuteNonQuery();
}
catch
{
exists = false;
}
}
Source: Check if a SQL table exists
You can do something like this:
string strCheck = "SHOW TABLES LIKE \'tableName\'";
cmd = new MySqlCommand(strCheck, connection);
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
cmd.Prepare();
var reader = cmd.ExecuteReader();
if (reader.HasRows)
{
Console.WriteLine("Table Exist!");
}
else (reader.HasRows)
{
Console.WriteLine("Table Exist!");
}

Get id when inserting new row using TableAdapter.Update on a file based database

I have a database table with one field, called ID, being an auto increment integer.
Using a TableAdapter I can read and modify existing rows as well as create new ones.
However if I try to modify a newly inserted row I get an DBConcurrencyException:
OleDbConnection conn = new OleDbConnection(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Shift.mdb;Persist Security Info=True");
ShiftDataSetTableAdapters.ShiftTableAdapter shiftTA = new ShiftDataSetTableAdapters.ShiftTableAdapter();
shiftTA.Connection = conn;
ShiftDataSet.ShiftDataTable table = new ShiftDataSet.ShiftDataTable();
ShiftDataSet.ShiftRow row = table.NewShiftRow();
row.Name = "life";
table.Rows.Add(row);
shiftTA.Update(row); // row.ID == -1
row.Name = "answer"; // <-- all fine up to here
shiftTA.Update(row); // DBConcurrencyException: 0 rows affected
Separate question, is there any static type of the NewShiftRow() method I can use so that I don't have to create table everytime I want to insert a new row.
I guess the problem in the code comes from row.ID that is still -1 after the first Update() call. The Insert is successful and in the database the row has a valid value of ID.
How can I get that ID so that I can continue with the second Update call?
Update:
IT looks like this could have been done automatically using this setting.
However according to the answer on msdn social, OLEDB drivers do not support this feature.
Not sure where to go from here, use something else than oledb?
Update:
Tried SQLCompact but discovered that it had the same limitation, it does not support multiple statements.
Final question: is there any simple(single file based) database that would allow you to get the values of a inserted row.
Try this http://support.microsoft.com/kb/815629 , the sample code is in VB.NET though.
Or if multiline query is accepted in MS Access and it has built-in function/variable for retrieving the last id, use this (the database is SQLite though): anyway see why I get this "Concurrency Violation" in these few lines of code??? Concurrency violation: the UpdateCommand affected 0 of the expected 1 records , try to google for the function
[EDIT: Works on my Machine, I don't have SQL Server Compact, but I didn't use multi-statement]
public Form1()
{
InitializeComponent();
var c = Connect();
var da = new SqlDataAdapter("select emp_id, emp_firstname, emp_lastname from emp where 1 = 0", c);
var b = new SqlCommandBuilder(da);
var getIdentity = new SqlCommand("SELECT CAST(##IDENTITY AS INT)", c);
da.InsertCommand = b.GetInsertCommand();
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.RowUpdated += (xsender, xe) =>
{
if (xe.Status == UpdateStatus.Continue && xe.StatementType == StatementType.Insert)
{
xe.Row["emp_id"] = (int)getIdentity.ExecuteScalar();
}
};
var dt = new DataTable();
da.Fill(dt);
var nr = dt.NewRow();
nr["emp_firstname"] = "john";
nr["emp_lastname"] = "lennon";
var nrx = dt.NewRow();
nrx["emp_firstname"] = "paul";
nrx["emp_lastname"] = "mccartney";
dt.Rows.Add(nr);
dt.Rows.Add(nrx);
da.Update(dt);
dt.AcceptChanges();
nrx["emp_lastname"] = "simon";
da.Update(dt);
nr["emp_lastname"] = "valjean";
da.Update(dt);
}
SqlConnection Connect()
{
return new SqlConnection(#"data source=.\SQLEXPRESS;Database=Test;uid=sa;pwd=hey");
}
Why not select the MAX(RowId), as your RowId should increment for each INSERT? Is this possible for you?
As for your final answer, SQLite might be the perfect tool for you. I hope so! And it has its own .NET Data Provider, so no need for OLEDB or ODBC providers.

Categories