I'm trying store data in a local database(SQL CE). I've set a property of RowObject like this:
[Column(IsPrimaryKey=true,CanBeNull=false,IsDbGenerated=true,AutoSync=AutoSync.OnInsert,DbType="int NOT NULL IDENTITY")]
public int key
{ get; set; }
and the primary key is automatically auto-incremented when I insert a new record. But if I remove one or more record and then I insert a new record, its primary key value corresponds at the primary key value of the last inserted record plus one, although that record was deleted.
I know it is not possible to update all primary keys. But, is it possible to control the increment of the primary key?and avoid the continuous increment of the primary key?
ok, thank you for your answer. I ask because I'm worried that PK reaches its maximum value. Maybe this isn't a really problem for my project, but I want understand how handle it, in case it happens. And in case, if it is possible, how can I understand and handle if the database is completely full.
that's violating auto increment.
I sometimes due to need disable auto incrementation of a column to add a row which is deleted on the old place and then enable it again.
If you insist on doing so, you can add 2 triggers, in the delete trigger disable column's auto increment behaviour, then enable it again on another trigger which is after insert trigger.
So on delete disable it, on next insert add it with the max(ID)+1 and after insert enable it again.
For example:
-------------Person Table--------------
ID[Auto-Increment]----Name----Age
10000-------------------Jack-------21
10001-------------------Shawn----19
10002-------------------Albert-----33
Delete from Person where ID = 10002;
set identity_insert AAA ON
now the next ID will be 10003 but you want 10002
DECLARE #ID int;
select #ID = max(ID)+1 from Person
Insert into Person(ID,Name,Age) values(#ID,'Mahdi',23)
set identity_insert AAA OFF --Auto Increments from now on
you should just check to see if you should insert your record with/without ID, I put that burden on your shoulder.
GOOD LUCK.
Related
This is Entity Framework 6.1.3
The SQL Server table has a two-column composite key.
ID INT Identity(1,1) NOT NULL
VERSION INT NOT NULL
Inserting a new record works because I don't set the ID on my object; I only set the VERSION.
So a new record would look like this:
ID VERSION
1 1
Perfect! The database generates the ID because the column is configured with Identity and my model is decorated with [DatabaseGenerated(DatabaseGeneratedOption.Identity)].
But now I need to insert another row with the same ID but a different VERSION; hence the composite key. So I would expect the second row to be:
ID Version
1 1
1 2 <- second row has same ID and different version
I do need this to work both ways because there is the scenario where a new ID should be auto-generated by the database, and the other scenario where I have the same ID but a different VERSION.
The Problem:
Because my Code-First model has the ID configured with DatabaseGeneratedOption.Identity, when I set the ID property on my object, my SaveChanges generates the insert statement without the ID!
(Diagnostic Tools in VS shows that Entity Framework generated this statement)
ADO.NET: Execute Reader "INSERT [dbo].[T1]([Version], ... VALUES (#0, ...)
Note the omission of ID. Because I explicitly set the ID on my object, I expected to see this statement instead.
INSERT [dbo].[T1]([ID], [Version], ... VALUES (#0, #1, ...)
That's what I'm trying to accomplish.
The question is:
How can I make Entity Framework include that ID column in its generated insert statement in an elegant way?
I don't want to use a stored procedure or hard code a SQL statement or hack the insert statement by 'squeezing in' the column.
If there is no way, I know that I would have to remove the use of Identity altogether and define my own IDs, which I'm trying to avoid.
Also, my SaveChanges() already makes use of SET IDENTITY_INSERT ON/OFF so that isn't any problem.
Here is the relevant part of my model: (I omitted other properties)
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Key, Column(Order = 1)]
public int VERSION { get; set; }
One avenue that I've explored was to reset my DbContext with a twist in OnModelCreating, but that didn't make a difference.
Of course, in that revision I did remove the DatabaseGenerated decorator off my ID property in the class. I inserted this into OnModelCreating:
if (this.AllowIdentityInsert)
{
modelBuilder.Entity<T1>().Property(x => x.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
else
{
modelBuilder.Entity<T1>().Property(x => x.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
If I could successfully control the model by changing the ID property to DatabaseGeneratedOption to None before my SaveChanges, then this could work and be an elegant solution.
Has anyone run into this situation and found a good solution?
Thanks so much for your input or suggestions.
Generally you don't want to use an identity column in that manner but I suppose if you are using a composite key you could. The problem that you will be faced with to insert your second record is that you will have to turn IDENTITY_INSERT on and off. So thinking of the SQL of it here is an example to show you what has to be done to accomplish the task.
IF OBJECT_ID('tempdb..#TblName') IS NOT NULL
BEGIN
DROP TABLE #TblName
END
CREATE TABLE #TblName (
ID INT IDENTITY(1,1) NOT NULL, Version INT NOT NULL
)
INSERT INTO #TblName (Version) VALUES (1)
SET IDENTITY_INSERT #TblName ON
INSERT INTO #TblName (ID, Version) VALUES (1,2)
SET IDENTITY_INSERT #TblName OFF
SELECT *
FROM
#TblName
A more typical design is to actually maintain a log table via a trigger and store the history in it. Because in that table it wouldn't need the identity column simply another INT.
There are a few more 2 table designs to get around the limitation but you may also want to look into creating SQL SEQUENCE https://msdn.microsoft.com/en-us/library/ff878058.aspx and instead of using IDENTITY on the ID column retrieving a SEQUENCE when you need it and always inserting the value. If you use a SEQUENCE you get the added benefit of being able to add another IDENTITY column that will be a local table ID which is typically recommended rather than relying solely on the composite key.
Okay here is (to me) a very interesting way of doing getting around your IDENTITY issue and maintaining a "incremented version". You can use an Update able View instead of directly using your table. You would use 2 SEQUENCES one for ID and one for VersionId and then to get Version you would use ROW_NUMBER() in the view. You could expand this solution by adding INSTEAD OF INSERT/UPDATE trigger to handle setting of the IDS more automatically but I don't generally like triggers. Anyway, here is to me an interesting solution:
CREATE TABLE dbo.yourTable (
TableId INT NOT NULL IDENTITY(1,1)
,Id INT NOT NULL
,VersionId INT NOT NULL
,Col VARCHAR(100) NOT NULL
,PRIMARY KEY (Id, VersionId)
)
GO
CREATE SEQUENCE dbo.SEQ_yourTableIdBy1
START WITH 1
INCREMENT BY 1;
GO
CREATE SEQUENCE dbo.SEQ_yourTableVersionIdBy1
START WITH 1
INCREMENT BY 1;
GO
CREATE VIEW dbo.yourTable_v
AS
SELECT
Id
,VersionId
,Version = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY VersionId)
,Col
,LatestVersion = CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Id ORDER BY VersionId DESC) = 1
THEN 1 ELSE 0 END
FROM
dbo.yourTable
GO
--New Record
INSERT INTO dbo.yourTable_v(Id, VersionId, Col)
VALUES (NEXT VALUE FOR dbo.SEQ_yourTableIdBy1, NEXT VALUE FOR dbo.SEQ_yourTableVersionIdBy1, 'A')
SELECT * FROM dbo.yourTable_v
--Change To Existing Record
INSERT INTO dbo.yourTable_v(Id, VersionId, Col)
VALUES (1, NEXT VALUE FOR dbo.SEQ_yourTableVersionIdBy1, 'B')
SELECT * FROM dbo.yourTable_v
link showing how it works http://rextester.com/GBHG23338
To Make Entity Framework believe the view is a table you may need to change the Key definition and the Entity Type here is a msdn blog on the subject. https://blogs.msdn.microsoft.com/alexj/2009/09/01/tip-34-how-to-work-with-updatable-views/
Benefits:
this isn't going to break if 2 people try to submit simultaneously etc.
Entity Framework will think this is a normal table once you fake it out slightly by following the link.
While it will have VersionId incremented across all records it will still present a nice Version + 1 for use in your application.
And you can easily add a latest version column for use in your application/queries.
With LINQ, I'm trying to delete a selected row in datagrid from database (made with code first) using db.Dishes.Remove(Dish);
But when I delete the item and inserting a new one, primary key (id) of new item "jumps" a value.
E.g.
1 Shoes
2 Jeans //I delete this item
When adding a new Item
1 Shoes
3 T-Shirt //jumps a value for Id
I've tried with this too in my DBContext.cs
modelBuilder.Entity<Cart>()
.HasOptional(i => i.Item)
.WithMany()
.WillCascadeOnDelete(true);
But it's not working
Is there a better way to delete an item from database?
The thing is that when we use DELETE it removes the row from the table but the counter is not changed (if the deleted row has an auto increment PK) see DELETE vs TRUNCATE
.
So, if you want to reuse the key value then you could do something on the lines of:
1) Handle the Auto Increment part of Key in your code
2) If you have
access to DB or want to query it something on the lines of this will
might be of help (SQL Server) :
DBCC CHECKIDENT ('tablename', RESEED, newseed)
to do this from code you could after the delete do :
db.ExecuteCommand("DBCC CHECKIDENT('tablename', RESEED, newseed);")
where 'newseed' is the id of the deleted row.e.g if newseed is 0 then next insert will be 1 and if it is 10 then the insert will have 11. To get the new seed value you could also get the max id value residing in your db and then work from there. Better check out what approaches you can take if you decide to go down that road.
From Reset autoicrement in SQL Server and how to use it in code.
If your primary key is an auto integer, you cannot avoid this behavior. This is simply how the database works. If you want to control the int value, do NOT make it the primary key and do not use auto integer. Instead use uniqueidentifier as your primary key and make your int a normal field. Then when you create your new records, you need to have a robust mechanism to get the next index, lock it so nobody else can steal it, and then write your record.
This is not trivial in a multi-threaded environment! You should do some research on the topic and come up with a good scheme. Personally, I'd NEVER attempt to do this and would use a repeatable process to generate numbers that are non-sequential or unique to a thread.
The primary key has to be unique (by definition), and you have also defined it as an identity column.
So when you delete a row and create a new one, that new one will take the next available key (3 in your case).
If you don't want this behaviour you will have to manage the uniqueness of the primary key yourself.
I have a ward table and it contains columns like wardid, wmemname, isActive, and so on... wardid is the primary key and isActive is boolean.
If the user deletes the record from the front end, I set isActive to false.
"Record is deleted successfully"
This message is shown to the user.
In my delete procedure I wrote an update statement to make isActive set to false:
update wardtable
set isActive = false
where wardid = #wardid
Fine up to here. In case the user wants to enter the details with the deleted wardid, now there is a problem with a primary key violation message from the front end when he tries to enter the data with the deleted wardid.
How to solve this issue?
Suppose if I take another id in my ward table then, it allows if I make id as an autoincrement. But duplicate wardid's are allowed in my ward table if I do like that.
So, what is the best way to do this?
Your doing too much with your primary key.
Create a real primary key of type uniqueidentifier / long and auto generated if need be.
Wardid should NOT be your primary key for this table, use your business logic to lookup data on this column and update / delete / insert as required. leave the new primary key for use by your database only. If wardid needs to be unique, make it a unique column by adding it to a unique index or use business logic.
To add on to uk2k05's answer an example of how you would implment it would be
CREATE TABLE dbo.wardtable
(
wardTableId bigint NOT NULL IDENTITY (1, 1),
wardid int NOT NULL,
deletedUniqueifier uniqueidentifier NOT NULL default('00000000-0000-0000-0000-000000000000'),
wmemname varchar(50) NOT NULL,
isActive AS (case when deletedUniqueifier = '00000000-0000-0000-0000-000000000000' then 1 else 0 end) PERSISTED
)
ALTER TABLE dbo.wardtable ADD CONSTRAINT
PK_wardtable PRIMARY KEY CLUSTERED (wardTableId)
CREATE UNIQUE NONCLUSTERED INDEX UX_wardtable_wardid
ON dbo.wardtable (wardid, deletedUniqueifier)
Now that isActive is a computed column your update would also have to change to
update wardtable
set deletedUniqueifier = newid()
where wardid = #wardid and deletedUniqueifier = '00000000-0000-0000-0000-000000000000'
One other logical change you will need to do, any forgen keys that link to this table should link using wardTableId instead of wardid
I'm trying to insert order details into my DB, and it keeps saying:
Cannot insert explicit value for identity column in table 'Orders' when IDENTITY_INSERT is set to OFF.
All I am trying to do is simply insert the users UserId into the UserId column, by using WebSecurity.CurrentUserId - Why is this not working?
I have:
dbase.Execute("INSERT INTO Orders
(UserId, OrderId, Status)
VALUES
(#0, #1, #2)",
WebSecurity.CurrentUserId,
Session["OSFOID"],
"Confirmed");`
So, as you can see, it's pretty simple. But, why won't it work?
My table definition is:
Unless you enable the ability to do identity-insert (by setting identity-insert on for that table), you are NOT ALLOWED to touch that column - the database owns it.
Either enable identity insert briefly, or: don't try to insert the UserId (let the DB create a new id).
As per books online, SET IDENTITY_INSERT:
SET IDENTITY_INSERT Orders ON
INSERT INTO Orders (UserId, OrderId, Status) VALUES (#0, #1, #2)
SET IDENTITY_INSERT Orders OFF
More likely, though: if this is the Orders table, should the identity not be on OrderId ? You'd still have the same problem since you are trying to control the OrderId, of course.
Generally you would not want to insert an integer into a primary key column. You would usually set the column's "Identity" flag to true only where you wanted to have SQL Server set an auto-incrementing integer into this column on insert.
As with Marc Gravell's answer, you can enable identity insert using
SET IDENTITY_INSERT [ database. [ owner. ] ] { table } { ON | OFF }
But doing this in regular application code is really unhealthy -- you'll end up with concurrency issues and quite likely duplicate identities. Better, don't insert the Order's ID -- let the DB do it for you automatically, and then simply query for the new ID using ##IDENTITY (or better, SCOPE_IDENTITY()).
If for some reason you definitely need to store the user's session id, make this a separate column on the order table, or better still, on a separate User table, with the UserId being a foreign key.
You do not want the UserID to be an Identity, and I also do not think you want the UserID to be the primary key either. More than likely, you want the OrderID to be the primary key, or at best shared primary key with the userid and orderid.
In your table's definition set Is Identity? as False, with out setting that you cant insert a value manually to the UserID
You have set IS Identity to YES that's why now you cant insert value for this column DB will automatically insert it by incrementing values..
And the thing i am seeing you set UserId as the primary key of the table which is wrong Order OoderID should be the primary key of the column.
Set UserID IsIdentify to false
and OrderID IsEdentitfy to yes and made it primary key column.
Interestingly I found that when I created a table using a "Select.....Into [new table] from [tables containing data]" I subsequently could not Insert new records, getting the Insert_Identity set to off message.
I got around this by deleting the table then using a Create Table script to rebuild it and now have no problems inserting as many new IDs as needed
I'm Developing a small windows application in C#.net in Visual Studio 2010 with framework 3.5. I use LinqToSql for database manipulation.
table name: cprofile
Fields of the table are:
custid int (primary key),
custname varchar(50),
address nvarchar(MAX),
mobileno nchar(10)
So i have changed the 'Is identity' property of the 'cust id' to 'yes'. It automatically changes other 2 sub properties.
Identity Increment = 1
Identity Seed = 1,
After these changes have been made in the table, it throws error when I try to save a new record.
"Cannot insert explicit value for identity column in table 'cprofile'
when IDENTITY_INSERT is set to OFF."
Not too familiar with L2S, but I'd say Daniel is correct: update your model (usually on a context menu somewhere) from the DB. That should prevent it from attempting to insert a value into your auto-incrementing ID column.
I believe there may be a way to have it set IDENTIY_INSERT ON, but I highly recommend against it.
If your table should not be in charge of setting the CustomerId (say, the business has some method of making that determination (especially in a non-linear way), leave your Customer Id column as the PK, but remove the Identity specificaiton from the column.
If you're trying to use the same insert statement you were using before, you can no longer do that. I'm not sure how it's done in the C# side of it, but in SQL, you'd have to run statements to turn identity_insert on, then run your statement. Because you changed the column to identity, the table makes sure the next entry is always 1 number higher than the previous. Because of this, you can't simply insert values into it. If you want the table to create the identity value for you, simply remove it. If my explanation doesn't help, hopefully this will.
Table Definition (Table1)
Col1 Identity
Col2 varchar(50)
Col3 bool
Insert statement before identity
INSERT INTO Table1 VALUES (1, 'Test', TRUE)
Insert statement after identity
INSERT INTO Table1 VALUES ('Test', TRUE)
When identity is on, you cannot specify the value without turning on identity_edit. I'll see if I can find how to do that in Linq.
EDIT: I also like what Daniel said. Didn't think about that.