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.
Related
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.
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.
I am using Dapper with C# and back end is MS Access. My DAL method inserts record in database. I want to return unique identifier (or updated POCO with unique identifier) of the inserted row.
I am expecting my function something like follows (I know this does not work; just to explain what I want): -
public MyPoco Insert(MyPoco myPoco)
{
sql = #"INSERT INTO MyTable (Field1, Field2) VALUES (#Field1, #Field2)";
var param = GetMappedParams(myPoco);//ID property here is null.
var result = _connection.Query<MyPoco>(sql, param, null, false, null, CommandType.Text);.Single();
return result;//This result now contains ID that is created by database.
}
I am from NHibernate world and POCO updates automatically with NH. If not; we can call Refresh method and it updates the ID.
I am not aware how to achieve this with Dapper.
I read this question on SO which is not relevant as it talks about SQL Server.
Another this question does not have accepted answer.
I read this question where accepted answer explains pit-falls of using ##Identity.
This is what works for me:
static MyPoco Insert(MyPoco myPoco)
{
string sql = "INSERT INTO MyTable (Field1, Field2) VALUES (#Field1, #Field2)";
_connection.Execute(sql, new {myPoco.Field1, myPoco.Field2});
myPoco.ID = _connection.Query<int>("SELECT ##IDENTITY").Single();
return myPoco; // This result now contains ID that is created by database.
}
Note that this will work with an OleDbConnection to the Access database, but it will not work with an OdbcConnection.
Edit re: comment
To ensure that the Connection remains open between the INSERT and the SELECT calls, we could do this:
static void Insert(MyPoco myPoco)
{
string sql = "INSERT INTO MyTable (Field1, Field2) VALUES (#Field1, #Field2)";
bool connAlreadyOpen = (_connection.State == System.Data.ConnectionState.Open);
if (!connAlreadyOpen)
{
_connection.Open();
}
_connection.Execute(sql, new {myPoco.Field1, myPoco.Field2});
myPoco.ID = _connection.Query<int>("SELECT ##IDENTITY").Single();
if (!connAlreadyOpen)
{
_connection.Close();
}
return; // (myPoco now contains ID that is created by database.)
}
Just a couple of extra thoughts: If the ##Identity pitfalls are an issue then another option would be to create a new GUID ahead of time in code and then insert that GUID with the rest of the data, rather than letting Access create the identity value when it creates the new record.
I appreciate that will only work if your particular situation allows for a GUID primary key for the table, but it does guarantee you that you know the true value of the key for the record you just inserted.
Alternatively, if you don't want a GUID key you could create a table with a single row that holds the current seed value for any manually managed keys in your application. You can then manually increment the particular seed's value each time you want to insert a new record. As with the GUID approach, you'd then manually insert the ID with the record, this time the ID would be the newly incremented seed you just retrieved.
Again, that should guarantee you a unique key for each insert, although now you are doing a read and two writes for each insert.
Using Text Area I'm sending Values to database by clicking button, but I want postID to auto generate how I can do it?
protected void btnPost_Click(object sender, EventArgs e)
{
string PostQuery = "INSERT INTO [Post] (PostID,PostDet,Votes,UserID) VALUES('<auto Genatare post ID>','" + TxtPost.Text + "','12','Us001')";
dbClass.ConnectDataBaseToInsert(PostQuery);
Response.Redirect("~/computing.aspx");
}
You could make PostID a UNIQUEIDENTIFIER column and then pass in a newly generated GUID (Guid.NewGuid()).
Also, please use parameterized queries to avoid SQL injection. Especially if the inputs come directly from WEB users.
To do so, change your ConnectDataBaseToInsert method to not take SQL text, but an SqlCommand which you prepare with the respective parameters.
From your comment to the question: The PostID should be like PO0001. Then the only way to do it properly and to respect for concurrency is to generate a stored procedure that takes the value to insert, which generates the ID itself.
To do so, create a new table that contains the last post ID. Then, use an UPDATE ... OUTPUT statement to increment and return in one go. This is the only way to do an atomic update of the post ID so that no two users create the same ID.
Example Table PostIDTable
Current
=======
0
Example SELECT to update and retrieve the current post ID:
-- We need a temp table, because OUTPUT can not output into a single variable
-- This increments Current by one and outputs the value that was set in one go.
-- This prevents simultaneous calls to get the same ID
DECLARE #postID (ID INT)
UPDATE PostIDTable
OUPUT INSERTED.Current INTO #postID
SET
Current = Current + 1
-- Get the value from the temp table and convert it into the desired format
DECLARE #pID INT = (SELECT TOP 1 ID FROM #postID)
DECLARE #id NVARCHAR(6) = 'PO' + RIGHT('0000' + CONVERT(NVARCHAR, #pID), 4)
-- Do the actual INSERT
INSERT INTO (PostDet, Votes,UserID) VALUES (#id, ..., ...)
You should make the PostID column to be an IDENTITY(1,1) column (in case of using MSSQL. server). So when you will insert new rows to your database the corresponding PostID values will be autogenerated by your database. The same holds for each other RDBMS you may use.
Having done the above change you code will change to the following:
protected void btnPost_Click(object sender, EventArgs e)
{
string PostQuery = "INSERT INTO [Post] (PostDet,Votes,UserID) VALUES("TxtPost.Text + "','12','Us001')";
dbClass.ConnectDataBaseToInsert(PostQuery);
Response.Redirect("~/computing.aspx");
}
If PostId is autogenerated, only do this:
protected void btnPost_Click(object sender, EventArgs e)
{
string PostQuery = "INSERT INTO [Post] (PostDet,Votes,UserID) VALUES('" + TxtPost.Text + "','12','Us001')";
dbClass.ConnectDataBaseToInsert(PostQuery);
Response.Redirect("~/computing.aspx");
}
Removing PostId from your query, it will generate the next Id
the best way is to use auto increment column ID for your table.
Please, see w3school tutorial where is described construction for all main db.
Try this..
select * from tablename order by id desc limit 1
where id would be your primary key attribute name.
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