I am using dapper.rainbow for inserting a record into MSSQL db. Following is my code
int? id = db.RoomTypes.Insert(roomType)
When i run my app, i am getting the below exception.
Cannot insert explicit value for identity column in table 'RoomTypes'
when IDENTITY_INSERT is set to OFF.
I think that dapper.rainbow is using the value (default value for int) for my Identity Column during the insert. This is what causing this exception. My identity column for the table RoomTypes is auto incremented and it is also the primary key of my table.
Now, how do i stop the dapper.rainbow from using the ID column during insert.
Looking at the Dapper.Rainbow.Insert method, the method is "dumb" to the type passed in as the data parameter:
public virtual int? Insert(dynamic data)
{
var o = (object)data;
List<string> paramNames = GetParamNames(o);
string cols = string.Join(",", paramNames);
string cols_params = string.Join(",", paramNames.Select(p => "#" + p));
var sql = "set nocount on insert " + TableName + " (" + cols + ") values (" + cols_params + ") select cast(scope_identity() as int)";
return database.Query<int?>(sql, o).Single();
}
In other words, if you include the ID as a property in the parameter, then Dapper will try to insert that value into the database. And as you've seen, if you have IDENTITY_INSERT set to Off, then the insert will fail.
This means you'll need to create a new type that doesn't include your ID property to pass into the method. A few of ideas come to mind:
Create a base class without the ID for RoomTypes and then just pass in the base class to the Insert method.
Create a dynamic parameter and pass that to the Insert method with only the properties you want to insert into the database.
Override the Insert method and remove the ID parameter before sending to the Dapper
Related
I've done a POST method that takes object Recipe and inserts its values in the database using Dapper. I've written the SQL query so that when a new entry is inserted, the ID value will be autogenerated as: the maximum existing value in the database + 1. See code below:
using (var con = _connFactory())
{
con.Open();
con.Execute(#"INSERT INTO dbo.Recipe (Id, Name, RecipeLink, Category1Id ,Category2Id, Category3Id, Category4Id, RecipeById,
TotalTime, TotalTimeUnitId, ActiveTime, ActivetimeUnitId, Instructions, SourceKey, RecipeBy, InsertedAtUtc, IsVerified, NumPersons, ServingsUnitId, ServingsUnitOrig)
VALUES ((SELECT MAX(ID) + 1 FROM dbo.Recipe), #name, #recipeLink, #category1Id, #category2Id, #category3Id, #category4Id, #recipeById,
#totalTime, #totalTimeUnitId, #activeTime, #activeTimeUnitId, #instructions, #sourceKey, #recipeBy, getutcdate(), #isVerified, #numPersons, #servingsUnitId, #servingsUnitOrig)",
new
{
...
...
});
}
When I send the request to the API using Postman, the returned result will be the newly created object that has been passed to the database. However, though, because of the way that my ID is created, the returned JSON looks like this:
{
"id": 0,
"name": "Test Recipe2",
"recipeLink": "testlink",
"category1Id": 7757,
...
"servingsUnitId": 3,
"servingsUnitOrig": null
}
As you can see, the ID is 0. But if I try to get the object after this, the ID will be set to the proper one, the one generated from (SELECT MAX(ID) + 1 FROM dbo.Recipe).
Is there any way in which I can make the API return the correct ID after the execution of the INSERT INTO? Or how should I change the logic behind the auto generation in order to achieve this?
It will come quite in handy if I was able to get the ID's value directly as now I've made a method that returns the ID of the latest Recipe created. But that might be a problem in case that there are 2 recipes created in a short period of time.
You can return the Id and use ExecuteScalar to get it. In the execute Statement declare & generate next Id and the the end select the NewId value
using (var con = _connFactory())
{
con.Open();
var addedId = con.ExecuteScalar(#"DECLARE #NewId INT = (SELECT ISNULL(MAX(ID),0) + 1 FROM dbo.Recipe);INSERT INTO dbo.Recipe (Id, Name, RecipeLink, Category1Id ,Category2Id, Category3Id, Category4Id, RecipeById,
TotalTime, TotalTimeUnitId, ActiveTime, ActivetimeUnitId, Instructions, SourceKey, RecipeBy, InsertedAtUtc, IsVerified, NumPersons, ServingsUnitId, ServingsUnitOrig)
VALUES (#NewId, #name, #recipeLink, #category1Id, #category2Id, #category3Id, #category4Id, #recipeById,
#totalTime, #totalTimeUnitId, #activeTime, #activeTimeUnitId, #instructions, #sourceKey, #recipeBy, getutcdate(), #isVerified, #numPersons, #servingsUnitId, #servingsUnitOrig); SELECT #NewId",
new
{
...
...
});
//use addedId here...
}
I would strongly suggest that you change the Id behavior to use either SQL-Server Sequence or IDENTITY. Using select max() can generate duplicates
Select the id and call Query to retrieve it:
const string Sql = #"
INSERT INTO dbo.Recipe (...) VALUES (....);
SELECT CAST(SCOPE_IDENTITY() as int)";
int id = con.Query<int>(Sql, new { ...}).Single();
You can use SQL Auto Increment and change table
https://www.w3schools.com/sql/sql_autoincrement.asp
ALTER TABLE dbo.Recipe DROP COLUMN Id
ALTER TABLE dbo.Recipe ADD Id INT IDENTITY(1,1)
After that, you don't need a pass id field in your query.
con.Execute(#"INSERT INTO dbo.Recipe (Name, RecipeLink, Category1Id ,Category2Id, Category3Id, Category4Id, RecipeById,
TotalTime, TotalTimeUnitId, ActiveTime, ActivetimeUnitId, Instructions, SourceKey, RecipeBy, InsertedAtUtc, IsVerified, NumPersons, ServingsUnitId, ServingsUnitOrig)
VALUES (#name, #recipeLink, #category1Id, #category2Id, #category3Id, #category4Id, #recipeById,
#totalTime, #totalTimeUnitId, #activeTime, #activeTimeUnitId, #instructions, #sourceKey, #recipeBy, getutcdate(), #isVerified, #numPersons, #servingsUnitId, #servingsUnitOrig)"
You have trouble because of your first record
SELECT ISNULL (MAX (ID), 0) + 1 FROM [dbo]. [Recipe]
I develop a program who save a picture. But I get this error message:
Cannot insert the value NULL into column 'Id'
private void savepicture()
{
if(pictureBox1.Image != null)
{
MemoryStream ms = new MemoryStream();
pictureBox1.Image.Save(ms, pictureBox1.Image.RawFormat);
byte[] a = ms.GetBuffer();
ms.Close();
cm.Parameters.Clear();
cm.Parameters.AddWithValue("#picture", a);
cm.CommandText = "insert into Bild (FileName,Datei) values ('" + label1.Text.ToString() + "',#picture )";
sc.Open();
cm.ExecuteNonQuery(); // i get here the error message
sc.Close();
label1.Text = "";
pictureBox1.Image = null;
MessageBox.Show("Bild wurde gespeichert !");
}
}
Before anything you should check for a structure of your table Bild,
there you probably have a column called Id which is constraint and can not be a null.
And by reading your code I can not see on next line that you are supplying 'Id' to your sql:
cm.CommandText = "insert into Bild (FileName,Datei) values ('" + label1.Text.ToString() + "',#picture )";
What you should do here is next:
Send id from your code to a sql (create variable and include it in insert command) depending of your column type (int, guid or whatever, so if you are using int you should incremet your variable which will hold value for next 'Id' in your database, or if it is Guid you should create new Guid for every new row using Guid.NewGuid method )
Edit id column on Sql and apply AUTO_INCREMENT keyword to perform an auto-increment feature, example :
Alter TABLE Bild(
id int NOT NULL AUTO_INCREMENT,
rest of columns
);
Or you might apply next steps in case you are using Microsoft SQL and I guess you do because I can see you are using SqlCommand.CommandText Property in your C# code.
Presumably you are in the design of the table. If not: right click the
table name - "Design". Click the required column. In "Column
properties" (at the bottom), scroll to the "Identity Specification"
section, expand it, then toggle "(Is Identity)" to "Yes".
I cannot add comments so i will answer here.
If your Id is the primary key, you either insert it yourself or use the identity option (auto increment):
add the identity option to the key from ssms. If you want to do it by query follow the answer given here https://dba.stackexchange.com/questions/128433/add-autoincrement-to-existing-pk
in your insert statement do insert into Bild (Id,FileName,Datei) values ([your id], 'val1', 'val2').
Note: You have to specify the Id you want to insert if your primary key does not have an way of doing it itself (auto increment)
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 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.
I have two tables one is members table with columns member id , member first name, member last name. I have another table guest passes with columns guest pass id and member id and issue date .
I have a list view that will displays guest passes details (I.e) like member name and issue date and I have two text boxes those are for entering member name and issue date .
member name text box is auto complete text box that working fine....
but the problem is when I am entering the name that is not in member table at this time it will accept and displays a blank field in list view in member name column and member id is stored as "0" in guest pass table ......
I don't want to display the member name empty blank and I don t want to store "0" in guest pass table
and this is the insert statement
sql2 = #"INSERT INTO guestpasses(member_Id,guestPass_IssueDate)";
sql2 += " VALUES(";
sql2 += "'" + tbCGuestPassesMemberId.Text + "'";
sql2 += ",'" + tbIssueDate.Text + "'";
guestpassmemberId = memberid
is there any validation that need to be done
and this is the auto complete text box statement
sql = #"SELECT member_Id FROM members WHERE concat(member_Firstname,'',member_Lastname) ='" + tbMemberName.Text+"'";
dt = GetData(sql, mf);
if (dt != null)
{
if (dt.Rows.Count > 0)
{
tbCGuestPassesMemberId.Text = Convert.ToInt32(dt.Rows[0] ["member_Id"]).ToString();
}
}
can any one help me on this ...
is there any type of validation with sql query
pls help me .....
You can validate the values before passing them to the INSERT. Additionally you can also set a constraint to validate versus a regular expression in SQL.
SQL constraints
To validate before inserting you should have something like this:
private void validateData(Long memberId) {
//Pseudo code Depends on how you are connecting to your database...
SQLQuery query = getQuery("existsMemberId");
query.setParameter("memberId");
executeQuery(query);
// If the query returns something then the reference exists and it is ok to proceed
}
In the file you are storing your queries...
#existsMemberId
select 1
from members mem
where mem.id = :id <-- parameter
Additionally you should make foreign key constraint between members and guest passes with ID as the foreign key:
ALTER TABLE GuestPasses
ADD CONSTRAINT fk_memberId
FOREIGN KEY (id)
REFERENCES Members(id)