Generate an unique ID - c#

I want to generate unique ID's for clients and store them in MySql database. It's a C# winform application, when one client enter his data like name etc. and when clicking on savebutton the ID generator needs to give him an unique ID which won't be repeated later.
I made it work, but after about 30 inserts, the generator drops a repeated number and the MySql gives this error: "Duplicate entry '76' for key 'PRIMARY'".
This is not a big error or crash, but can be annoying for long time use.
That's my code:
int minID = 1;
int maxID = 1000;
int resID;
Random CustomID = new Random();
resID = CustomID.Next(minID,maxID);
How can I check the saved ID's and clear the repeating first, then generate another number that's unique and not in the table yet?
MySql info (database table:ugyfelinfo, column: ID)
auto_increment works well, but with my method I was able to show to saved ID to the customer when he clicked save button. Like this:
MessageBox.Show("New Costumer ID: " + resID + " - Saved in Database!")
But, now because I have auto_increment in mysql, how can I display the delivered ID for user via MB?
The queries are like this:
string constring = "datasource=localhost;port=3306;username=root;password=root";
string Query = "insert into clients_db.ugyfelinfo (Ugyfel,... ) " +
" values('" + this.UgyfelTextBox.Text + "','" ..."') ;";
MySqlConnection conDataBase = new MySqlConnection(constring);
MySqlCommand cmdDataBase = new MySqlCommand(Query, conDataBase); etc.
And I tried something like this this:
string idquery = "SELECT from clients_db.ugyfelinfo (LAST_INSERT_ID();) ";
MessageBox.Show("New user ID: "+idquery+" saved in database!");

Probably the best solution is to use Guid.NewGuid() and save it into CHAR(16) column (see this post). Chance of duplicate entry is very close to zero and even if you have a bad luck and Guid.NewGuid() generates duplicate, you can re-run your insert with new value.
If you don't need your identifier to be globally unique or you need integer key, you have to transfer responsibility for ID generating to the database. You just insert new row without the id value and you query for the result. This post migh help you.

If you have single database then maybe use database generate unique id is better.
The code you showing there is no warranty it wont get duplicated.
If you want client side unique code without checking the database have a look at using GUID.

Related

C# - Implementing Enum within a button (create a new record)

I am trying to implement an Enum within a Save button (which inserts new records in a DB connected to the Visual Studio WinForms Application).
In this case, I have Books & Category Tables. Category is also a Foreign Key within Books table (as CategoryID, which should accept only int data type).
With that said, I would like the application to understand / convert a string value to an int value, for example:
If I input the value "History" within the Category Textbox in the application, and fill in the rest of the details required to insert a new book record in DB, and finally click Save Button, I need the application to understand that "History" would reflect an ID (in this case, ID: '4') in Category Table.
In short, I don't want to input int values in the application - I want to type in the Category Name in the CategoryID Textbox.
private void btnSave_Click(object sender, EventArgs e)
{
string ConnectionString = #"Data Source=.\SQLEXPRESS;AttachDbFilename=
C:\Program Files\Microsoft SQL
Server\MSSQL14.SQLEXPRESS\MSSQL\DATA\Library System Project.mdf
;Integrated Security=True;Connect Timeout=30";
string Query = "insert into Books (BookName, BookAuthor, CategoryID, ClassificationID, BookAvailabilityQuantity, Price) values ('" + this.txtName.Text.Trim() + "','" + this.txtAuthor.Text.Trim() + "','" + this.txtCategory.Text.Trim() + "','" + this.txtClassification.Text.Trim() + "','" + this.txtAvailabilityQuantity.Text.Trim() + "','" + this.txtPrice.Text.Trim() + "');";
SqlConnection DBCon = new SqlConnection(ConnectionString);
SqlCommand DBCommand = new SqlCommand(Query, DBCon);
SqlDataReader DBReader;
try
{
DBCon.Open();
DBReader = DBCommand.ExecuteReader();
MessageBox.Show("New book record added to the system.", "Library System", MessageBoxButtons.OK);
while (DBReader.Read())
{
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// *** If you're going to be opening a connection be sure to close it ***
// *** Finally blocks work well for this ***
DBCon.Close();
this.txtName.ResetText();
this.txtAuthor.ResetText();
this.txtCategory.ResetText();
this.txtClassification.ResetText();
this.txtAvailabilityQuantity.ResetText();
this.txtPrice.ResetText();
}
}
Create an enum for your categories, for example:
enum BookCategory
{
Science=1,
Art,
Romance,
History
}
Then you can write:
if(Enum.TryParse<BookCategory>(txtCategory.Text.Trim(), out BookCategory categoryId))
Console.WriteLine((int)categoryId);
else
Console.WriteLine("Category not recognised");
Which will print 4 if txtCategory contains "History".
If I understand your question correctly, there are different approaches for what you're trying to achieve.
Where do you want that 'enum' to originate from? You can actually make an enum with categories and their respectable value, and display it to the user to chose from, like:
public enum Categories
{
SciFi = 1,
...
History = 4,
...
}
as iakobski answered, and you can also get these categories from a database (thus, making them dynamic, e.g. you can add and remove categories from the list without the need to change static code, compile it and re-deploy).
Usually, the common practice in most cases is indeed storing such values in a database, in a table with columns of an ID and CategoryName.
If I input the value "History" within the Category Textbox in the
application, and fill in the rest of the details required to insert a
new book record in DB, and finally click Save Button, I need the
application to understand that "History" would reflect an ID (in this
case, ID: '4') in Category Table.
Unless you have business rules tied to specific emum values, I suggest to don't hard-code the values in the application and instead load the category name and id values from a database Category table. That can also ensure data integrity with a foreign key relationship to the Book table and allow new categories to be added without code changes.
A better user experience would be a combobox/list that the users to select from by name instead of free-form text box. The code can then use the id of the selected value without additional validation or lookup for the insert and the database.
On a separate note, use parameterized queries to improve performance and security. One should never build SQL queries using string concatenation from untrusted sources.

Database and DataSet are not synced regarding auto incrementing ID

I built a Database (Microsoft SqlServerCe.4.0) using Visual Studio and one table containing two fields:
id: int, primary key, not null, unique, no default value, identity
nom, we don't really care about this one
Then I built a DataSet containing this table as a DataTable and I have a DataAdapter like this :
marque_adapter = factory.CreateDataAdapter();
command = connection.CreateCommand();
command.CommandText = "SELECT * FROM " + DB_TABLE_MARQUE + ";";
marque_adapter.SelectCommand = command;
command = connection.CreateCommand();
command.CommandText = "UPDATE " + DB_TABLE_MARQUE + " SET nom = #nom WHERE id = #id;";
CreateAndAddParameterFromSource(command, "id", "id");
CreateAndAddParameterFromSource(command, "nom", "nom");
marque_adapter.UpdateCommand = command;
command = connection.CreateCommand();
command.CommandText = "DELETE " + DB_TABLE_MARQUE + " WHERE id = #id;";
CreateAndAddParameterFromSource(command, "id", "id");
marque_adapter.DeleteCommand = command;
command = connection.CreateCommand();
command.CommandText = "INSERT INTO " + DB_TABLE_MARQUE + " (nom) VALUES (#nom);";
CreateAndAddParameterFromSource(command, "nom", "nom");
marque_adapter.InsertCommand = command;
//...
data = new DataSet();
marque_adapter.Fill(data, DB_TABLE_MARQUE);
The problem arises when I try to insert a new row.
I do :
table.NewRow()
set the "nom" field
table.Rows.Add(newRow)
adapter.Update(dataSet, tableName)
If I don't do anything else, I have issues later when I try to get the ID of this row (I guess it will set it somewhere between the four instructions above).
I was expecting the DataTable to take care of generating one, but ...
So I tried remindind the DataTable to take care of the auto incrementing :
idColumn.Unique = true;
idColumn.AutoIncrement = true;
Now it works the first time, but when I run the program a second time, it starts counting from one again and I'm told that the ID should be unique. If I delete the database (the copy of the sdf file made by Visual), or if I delete the rows manually using Visual, it runs well the first time, and I get the same error after.
The problem really is when I try to save my DataSet, particularly when adding new rows (selecting, updating, deleting is fine).
Obviously I didn't get how to manage primary keys when the DataTable and the database are involved (the datatable alone is ok).
Particularly to sync the two ...
What did I miss ?
I am quite sure I have misunderstood something.
According to MSDN,
Bydefault, AcceptChanges is called implicitly after an update, and the original values in the row, which may have been AutoIncrement values assigned by ADO.NET, are lost.
So you need to create a strategy to merge the AutoIncremented value Either via ADO or getting back the incremented Id from Sql as output parameter and then merge the Identity column value as indicated in this MSDN Article.

Unique number identifier generation

I have to create logic for generation unique number identifier for records in database. id, generated in database is a separate column.
At this moment, when user calls "create record" action, I save new record, get its database id, generate record number using this id, then put it to the edit form.
Using this way means that all entity fields should be nullable to save record to database.
I don't like this way. I know that should be better way.
Is there a better practice to generate unique number identifier? What is possibility of generating non-unique random numbers?
Thank you
The pattern that you're using, of saving an empty record simply to get the ID, is not a good one.
The standard approach, and the one that I'd recommend, is for Create Record to simply display an empty form (the ID at this point will typically be 0). The user fills in the form and the data is only committed to the database when the user clicks Save. The ID should be an IDENTITY column.
A problem with your approach is that if users do not complete the form, you end up with lots of incomplete records in your database. And, of course, it makes it much more difficult to handle data validation and integrity.
An alternative approach, if you really must display the ID to the user, is to have a separate table containing a row with a "Next Record ID" column. This column can be incremented and returned as an atomic operation and used to populate the ID of your new record. You still don't create the real record, just increment this "Next Record ID" in your Create Record action. Using this approach, you can use the same approach for multiple entities by having separate rows for each in this "Record IDs" table. Bear in mind that if the user does not ultimately save the record to the database, an ID will still have been 'used up'. The numbers will still be unique and will be chronological but won't necessarily be contiguous.
I don't get it, but, if you are using the uniqueidentifier data type in your database, that translates to Guid in C#, so you can do:
public Guid CreateRecord(MyObject model) {
Guid newId = Guid.NewGuid();
MyTable tbl = new MyTable();
tbl.guid = newId;
// ... other columns
db.MyTable.AddObject(tbl);
db.SaveChanges();
return newId;
}
though what I normally do, is having the PrimaryKey as int and add a uniqueidentifier field named guid (that I use it publically instead the column_id) and remember to index that column.
Code for Table..
CREATE TABLE TblTransactions(
TId varchar(8),
TName varchar(50)
)
C# Code Behind…
protected void Page_Load(object sender, EventArgs e)
{
string id = GenerateId("TblTransactions", "TId", 8, "TRN");
// insert the id along with data in the table
Response.Write(id);
}
public string GenerateId(string TableName, string ColumnName, int ColumnLength, string Prefix)
{
SqlConnection con = new SqlConnection("server=.;integrated security=true;database=EBissCard");
string Query, Id;
int PrefixLength, PadLength;
PrefixLength = Convert.ToInt32(Prefix.Length);
PadLength = ColumnLength - PrefixLength;
Query = "SELECT '" + Prefix + "' + REPLACE(STR(MAX(CAST(SUBSTRING(" + ColumnName + "," + Convert.ToString(PrefixLength + 1) + "," + PadLength + ") AS INTEGER))+1," + PadLength + "),' ',0) FROM " + TableName;
SqlCommand com = new SqlCommand(Query, con);
con.Open();
if (com.ExecuteScalar().ToString() == "")
{
Id = Prefix;
for (int i = 1; i <= PadLength - 1; i++)
{
Id += "0";
}
Id += "1";
}
else
{
Id = Convert.ToString(com.ExecuteScalar());
}
con.Close();
return Id;
}
An idea to generate a unique number for a record is to use the time() in milliseconds (since a reference point of time, say, 01/01/2010).
However, if there are 2 records that are simultaneously getting updated, this may cause an issue. To solve this problem, if each of the user can be assigned a number (when creating the userID), a combination (concatenation) of that "user number" and time in milliseconds will give you the unique number you need.
Try the Random class from .net itself.

use serial field per user in the entity framework

I have an entity with code field. For each user that uses this entity, each entity the user insert must have code. So I wrote code that do the following logic:
1. if the user set the code field - then use his code.
2. if the user doesn't set the code field - read from the db the next serial code (starting from 1, searching the next serial code that doesn't already exists) and use this code.
The probelm is scenario like this:
Assuming the user have the ability to add two entities in single mouse click.
Assuming the next serial code should be "5".
In the first entity the user set code=5 and in the second entity the user doesn't set the code.
Because I am using the entity framework and there is one commit/save changes at the end of the logic, I Insert the first entity (the one with code=5) and for the second entity, searching the db for the next next serial code that doesn't already exists. The next serial code that doesn't already exists in the database is "5". So I set the second entity the code "5".
Eventually I came up with two entities with code=5 which is wrong.
I thought of ways to solve it.
One way is to do SaveChanges right after storing the first entity but this might make many calls to the db, and I am not sure for this solution.
Another way is to search in the DB and in the attached objects but I really don't know how to do it.
Does anyone has any better idea?
You can use an IDENTITY column to allow SQL Server to auto-populate it when the user doesn't specify one, and when you have one that they do specify you just put it in your INSERT/UPDATE query along with the other fields. It will save your value instead of creating a new one. You do need to check and make sure that the ID hasn't already been used before doing that if you have a unique constraint on that field or if you don't want to allow duplicates when the user specifies a value.
The following example uses a table called MyTable with 3 columns (ID int IDENTITY, FIRST_NAME varchar, LAST_NAME varchar). I would recommend using parameters instead of passing the values in the sql string as I did below since this was just an example and it was faster to put together that way.
String sSQL = "";
String sFields = "";
String sValues = "";
String sCode = "";
// sCode = ... (get user entered code)
String sFirstName = "John";
String sLastName = "Doe";
if (sCode != "")
{ //Add the user specified value and field for the identity column to the SQL
sFields = "ID, ";
sValues = sCode + ", ";
}
sFields += "FIRST_NAME, LAST_NAME";
sValues += "'" + sFirstName.Replace("'","''") + "', '" + sLastName.Replace("'","''") + "'";
sSQL = "INSERT INTO MyTable (" + sFields + ") VALUES (" + sValues + ")"
//execute the sql statement

Getting the Last Insert ID with SQLite.NET in C#

I have a simple problem with a not so simple solution... I am currently inserting some data into a database like this:
kompenzacijeDataSet.KompenzacijeRow kompenzacija = kompenzacijeDataSet.Kompenzacije.NewKompenzacijeRow();
kompenzacija.Datum = DateTime.Now;
kompenzacija.PodjetjeID = stranka.id;
kompenzacija.Znesek = Decimal.Parse(tbZnesek.Text);
kompenzacijeDataSet.Kompenzacije.Rows.Add(kompenzacija);
kompenzacijeDataSetTableAdapters.KompenzacijeTableAdapter kompTA = new kompenzacijeDataSetTableAdapters.KompenzacijeTableAdapter();
kompTA.Update(this.kompenzacijeDataSet.Kompenzacije);
this.currentKompenzacijaID = LastInsertID(kompTA.Connection);
The last line is important. Why do I supply a connection? Well there is a SQLite function called last_insert_rowid() that you can call and get the last insert ID. Problem is it is bound to a connection and .NET seems to be reopening and closing connections for every dataset operation. I thought getting the connection from a table adapter would change things. But it doesn't.
Would anyone know how to solve this? Maybe where to get a constant connection from? Or maybe something more elegant?
Thank you.
EDIT:
This is also a problem with transactions, I would need the same connection if I would want to use transactions, so that is also a problem...
Using C# (.net 4.0) with SQLite, the SQLiteConnection class has a property LastInsertRowId that equals the Primary Integer Key of the most recently inserted (or updated) element.
The rowID is returned if the table doesn't have a primary integer key (in this case the rowID is column is automatically created).
See https://www.sqlite.org/c3ref/last_insert_rowid.html for more.
As for wrapping multiple commands in a single transaction, any commands entered after the transaction begins and before it is committed are part of one transaction.
long rowID;
using (SQLiteConnection con = new SQLiteConnection([datasource])
{
SQLiteTransaction transaction = null;
transaction = con.BeginTransaction();
... [execute insert statement]
rowID = con.LastInsertRowId;
transaction.Commit()
}
select last_insert_rowid();
And you will need to execute it as a scalar query.
string sql = #"select last_insert_rowid()";
long lastId = (long)command.ExecuteScalar(sql); // Need to type-cast since `ExecuteScalar` returns an object.
last_insert_rowid() is part of the solution. It returns a row number, not the actual ID.
cmd = CNN.CreateCommand();
cmd.CommandText = "SELECT last_insert_rowid()";
object i = cmd.ExecuteScalar();
cmd.CommandText = "SELECT " + ID_Name + " FROM " + TableName + " WHERE rowid=" + i.ToString();
i = cmd.ExecuteScalar();
I'm using Microsoft.Data.Sqlite package and I do not see a LastInsertRowId property. But you don't have to create a second trip to database to get the last id. Instead, combine both sql statements into a single string.
string sql = #"
insert into MyTable values (null, #name);
select last_insert_rowid();";
using (var cmd = conn.CreateCommand()) {
cmd.CommandText = sql;
cmd.Parameters.Add("#name", SqliteType.Text).Value = "John";
int lastId = Convert.ToInt32(cmd.ExecuteScalar());
}
There seems to be answers to both Microsoft's reference and SQLite's reference and that is the reason some people are getting LastInsertRowId property to work and others aren't.
Personally I don't use an PK as it's just an alias for the rowid column. Using the rowid is around twice as fast as one that you create. If I have a TEXT column for a PK I still use rowid and just make the text column unique. (for SQLite 3 only. You need your own for v1 & v2 as vacuum will alter rowid numbers)
That said, the way to get the information from a record in the last insert is the code below. Since the function does a left join to itself I LIMIT it to 1 just for speed, even if you don't there will only be 1 record from the main SELECT statement.
SELECT my_primary_key_column FROM my_table
WHERE rowid in (SELECT last_insert_rowid() LIMIT 1);
The SQLiteConnection object has a property for that, so there is not need for additional query.
After INSERT you just my use LastInsertRowId property of your SQLiteConnection object that was used for INSERT command.
Type of LastInsertRowId property is Int64.
Off course, as you already now, for auto increment to work the primary key on table must be set to be AUTOINCREMENT field, which is another topic.
database = new SQLiteConnection(databasePath);
public int GetLastInsertId()
{
return (int)SQLite3.LastInsertRowid(database.Handle);
}
# How about just running 2x SQL statements together using Execute Scalar?
# Person is a object that has an Id and Name property
var connString = LoadConnectionString(); // get connection string
using (var conn = new SQLiteConnection(connString)) // connect to sqlite
{
// insert new record and get Id of inserted record
var sql = #"INSERT INTO People (Name) VALUES (#Name);
SELECT Id FROM People
ORDER BY Id DESC";
var lastId = conn.ExecuteScalar(sql, person);
}
In EF Core 5 you can get ID in the object itself without using any "last inserted".
For example:
var r = new SomeData() { Name = "New Row", ...};
dbContext.Add(r);
dbContext.SaveChanges();
Console.WriteLine(r.ID);
you would get new ID without thinking of using correct connection or thread-safety etc.
If you're using the Microsoft.Data.Sqlite package, it doesn't include a LastInsertRowId property in the SqliteConnection class, but you can still call the last_insert_rowid function by using the underlying SQLitePCL library. Here's an extension method:
using Microsoft.Data.Sqlite;
using SQLitePCL;
public static long GetLastInsertRowId(this SqliteConnection connection)
{
var handle = connection.Handle ?? throw new NullReferenceException("The connection is not open.");
return raw.sqlite3_last_insert_rowid(handle);
}

Categories

Resources