Linq to SQL: Why am I getting IDENTITY_INSERT errors? - c#

I'm using Linq to SQL. I have a DataContext against which I am .SubmitChanges()'ing. There is an error inserting the identity field:
Cannot insert explicit value for identity column in table 'Rigs' when IDENTITY_INSERT is set to OFF.
The only identity field is "ID", which has a value of 0. It's defined in the DBML as:
[Column(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
There are a few foreign keys, and I've verified they have values that jive with the foreign tables' content.
Why would I be getting this error?
Edit: Here is the query:
exec sp_executesql N'INSERT INTO [dbo].[Rigs]([id], [Name], [RAM], [Usage], [MoreInfo], [datetime], [UID])
VALUES (#p0, #p1, #p2, #p3, #p4, #p5, #p6)
SELECT [t0].[id], [t0].[OSID], [t0].[Monitors]
FROM [dbo].[Rigs] AS [t0]
WHERE [t0].[id] = #p7',N'#p0 int,#p1 varchar(1),#p2 int,#p3 varchar(1),#p4 varchar(1),#p5 datetime,#p6 int,#p7
int',#p0=0,#p1='1',#p2=NULL,#p3='4',#p4='5',#p5=''2009-03-11 20:09:15:700'',#p6=1,#p7=0
Clearly it is passing a zero, despite having never been assigned a value.
Edit: Adding Code:
Rig rig = new Rig();
int RigID;
try
{ // Confirmed these always contain a nonzero value or blank
RigID = int.Parse(lbSystems.SelectedValue ?? hfRigID.Value);
if (RigID > 0) rig = mo.utils.RigUtils.GetByID(RigID);
}
catch { }
rig.Name = Server.HtmlEncode(txtName.Text);
rig.OSID = int.Parse(ddlOS.SelectedValue);
rig.Monitors = int.Parse(txtMonitors.Text);
rig.Usage = Server.HtmlEncode(txtUsage.Text);
rig.MoreInfo = Server.HtmlEncode(txtMoreInfo.Text);
rig.RigsToVideoCards.Clear();
foreach (ListItem li in lbYourCards.Items)
{
RigsToVideoCard r2vc = new RigsToVideoCard();
r2vc.VCID = int.Parse(li.Value);
rig.RigsToVideoCards.Add(r2vc);
}
rig.UID = c_UID > 0 ? c_UID : mo.utils.UserUtils.GetUserByToken(this.Master.LiveToken).ID;
if (!mo.utils.RigUtils.Save(rig))
throw new ApplicationException("There was an error saving your Rig. I have been notified.");
hfRigID.Value = rig.id.ToString();
public static User GetUserByToken(string token)
{
DataClassesDataContext dc = new DataClassesDataContext(ConfigurationManager.ConnectionStrings["MultimonOnlineConnectionString"].ConnectionString);
return (from u in dc.Users
where u.LiveToken == token
select u).FirstOrDefault();
}
Also, I notice that when I UPDATE an existing rig (insertonsubmit), it doesn't update. Profiler doesn't even show any queries being run.

My theory of what happened is as follows:
You designed and created the DataBase
You created the DB Context using LINQ TO SQL in visual Studio
You forgot to set the auto-identity to your table
You fixed that by going to your DataBase and setting the auto-identity
But you forgot to recreate/update your LINQ TO SQL DB Context!!!
:D

Is your code setting the ID value explicitely to 0? (instead of leaving it untouched).
Update 1: As you posted on the updated version, linq2sql is clearly passing the value to the db. Here is one I haven't had any trouble with:
[Column(Storage="_CustomerID", AutoSync=AutoSync.Always, DbType="Int NOT NULL IDENTITY", IsDbGenerated=true)]
public int CustomerID
I just saw another one, and it has the same exact definition of the one you are using.
[Column(Storage="_TestID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int TestID
Update 2: Regarding updates, you are not supposed to do InsertOnSubmit for that. Just update the values and call .SubmitChanges (should be throwing an exception). On the insert it is really weird, as the property attributes you posted seems to be correct, so the Insert method linq2sql generates should be correct as well. I would try, re-adding the table on the designer again and verifying all the properties are correct.
Note that the generated insert method should look like (with a matchingRig_Insert):
private void InsertRig(Rig obj)
{
System.Nullable<int> p1 = obj.Id;
this.Rig_Insert(/* bunch of values */, ref p1);
obj.Id = p1.GetValueOrDefault();
}

Ensure the property Auto Generated Value in your dbml for that column is set to true. If not, LINQ will attempt to default the value based on the type.

If you are supplying the value of the IDENTITY column, you must add
SET IDENTITY_INSERT ON
before your SQL Statements, and
SET IDENTITY_INSERT OFF
after to turn it off. That goes for ANY raw SQL. If you used LINQ to create an object like:
var customer = new LinqContext.Customer;
customer.FirstName = "Bob";
customer.LastName = "Smith";
LinqContent.Customer.Add(customer);
LinqContext.SubmitChanges();
I believe that will work, forgive me if I have classes wrong. You get the basic idea.
Edit: Oops.. Sorry, didn't read the entire question, I take it back, tired and fatigued from work...

seems like your ID is being assigned somewhere (even if just defaulting to 0) - would you care for posting some of your LINQ code?

I was having the exact same problem.
My solution was to manually make the identity INT column into a nullable INT? column in the DBML designer, not in the actual table of the DB of course. Also set the DbType to NULL instead of NOT NULL. Like so:
[Column(Storage="_TestID", AutoSync=AutoSync.OnInsert, DbType="Int NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int? TestID
This works with all DB operations. When inserting simply set the identity column to NULL rather than to 0.

my case, I forgot to update my dbml file after setting Id column to identity.

Related

Using LINQ to insert data into a table that uses a sequence as primary key generator

I have a table which generates its primary key from a sequence (that just counts up from 0):
CREATE TABLE [dbo].[testTable](
[id] [int] NOT NULL,
[a] [int] NOT NULL,
CONSTRAINT [PK_testTable] PRIMARY KEY CLUSTERED ([id] ASC))
ALTER TABLE [dbo].[tblTestTable] ADD CONSTRAINT [DF_tblTestTable_id] DEFAULT (NEXT VALUE FOR [seq_PK_tblTestTable]) FOR [id]
I've used Visual Studio's O/R Designer to create the mapping files for the table; the id field is defined as:
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id", DbType="Int NOT NULL", IsPrimaryKey=true)]
public int id {…}
and now I'm trying to insert data via LINQ.
var testTableRecord = new testTable()
{
a = 1,
};
db.Connection.Open();
db.testTables.InsertOnSubmit(testTableRecord);
db.SubmitChanges();
Console.WriteLine($"id: {testTableRecord.id}");
The problem I'm encountering is, that LINQ seems unable to handle the id generation via sequence as it sets the id implicitly to 0 when inserting.
When I set the id to CanBeNull, the insert fails because it tries to insert NULL into a non-nullable field.
When I set the id to IsDbGenerated, the insert works but it expects an IDENTITY field and tries to load the generated id with SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]',N'#p0 int',#p0=1 and than sets the id in the object to null because SCOPE_IDENTITY() returns null…
I've been thinking about just using IsDbGenerated, destroying the LINQ object and querying the DB for the id, but I don't have anything unique to search for.
Unfortunately changing the id creation mechanism to IDENTITY is not an option.
Do I have to explicitly query the DB for the next sequence value and set the id manually?
Whats the best way to handle these inserts via LINQ?
PS: I need the id because I have to insert more data that uses the id as FK.
Looking at solutions from the raw sql perspective:
1.
INSERT INTO [dbo].[testTable] VALUES (NEXT VALUE FOR [dbo].[seq_PK_tblTestTable], 1)
Simply can't be done in LINQ to SQL as far as I can tell
2.
INSERT INTO [dbo].[testTable] (a) VALUES (1)
This can be achieved in LINQ to SQL by excluding the id property from the testTable entity.
If you need to retrieve ids from the table, you could create separate entities for inserting and querying:
public class testTableInsert {
[ColumnAttribute(...)]
public int a
}
public class testTableResult {
[ColumnAttribute(...)]
public int id
[ColumnAttribute(...)]
public int a
}
3.
DECLARE #nextId INT;
SELECT #nextId = NEXT VALUE FOR [dbo].[seq_PK_tblTestTable];
INSERT INTO [dbo].[testTable] VALUES (#nextId, 1)
As you mentioned, this can be essentially achieved by manually requesting the next id before each insert. If you go this route there are multiple ways to achieve it in your code, you can consider stored procedures or use the LINQ data context to manually execute the sql to retrieve the next sequence value.
Here's a code sample demonstrating how to extend the generated DataContext using partial methods.
public partial class MyDataContext : System.Data.Linq.DataContext
{
partial void InsertTestTable(TestTable instance)
{
using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "SELECT NEXT VALUE FOR [dbo].[seq_PK_TestTable] as NextId";
cmd.Transaction = Transaction;
int nextId = (int) cmd.ExecuteScalar();
instance.id = nextId;
ExecuteDynamicInsert(instance);
}
}
}
Once the above is implemented, you can safely insert entities like this, and they will generate the correct sequence id.
TestTable entity = new TestTable { a = 2 };
dataContext.TestTables.InsertOnSubmit(entity);
dataContext.SubmitChanges();
Your only hope is a pretty profound refactoring and use a stored procedure to insert records. The stored procedure can be mapped to the class's Insert method in the data context designer.
Using your table definition, the stored is nothing but this:
CREATE PROCEDURE InsertTestTable
(
#id int OUTPUT,
#a AS int
)
AS
BEGIN
INSERT dbo.testTable (a) VALUES (#a);
SET #id = (SELECT CONVERT(int, current_value)
FROM sys.sequences WHERE name = 'seq_PK_tblTestTable')
END
You can import this stored procedure into the context by dragging it from the Sql Object Explorer onto the designer surface, which will then look like this:
The next step is to click the testTable class and click the ellipses button for the Insert method (which got enabled by adding the stored procedure to the context):
And customize it as follows:
That's all. Now LINQ-to-SQL will generate a stored procedure call to insert a record, for example:
declare #p3 int
set #p3=8
declare #p5 int
set #p5=0
exec sp_executesql N'EXEC #RETURN_VALUE = [dbo].[InsertTestTable] #id = #p0 OUTPUT,
#a = #p1',N'#p0 int output,#p1 int,#RETURN_VALUE int output',
#p0=#p3 output,#p1=123,#RETURN_VALUE=#p5 output
select #p3, #p5
Of course you may have to wonder how long you're going to hang on to LINQ-to-SQL. Entity Framework Core has sequence support out of the box. And much more.

Any other optimal way to update the parent table's columns when a child table is updated?

I have a few table structure look as below:
CREATE TABLE Person
(
PersonID INT PRIMARY KEY,
Name NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
CREATE TABLE Info
(
InfoID INT PRIMARY KEY,
PersonID INT,
Info NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
CREATE TABLE Setting
(
SettingID INT PRIMARY KEY,
PersonID INT,
Setting NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
I face a new procedure to follow that if there is any updates on Info or Setting table, I will need to do relevant updates to Person table on columns LastUpdatedBy and LastUpdatedDate.
What first come to my mind is to create a SQL trigger that automatically update Person table when Info or Setting table does. But take a quick glance through for a few articles stating that a SQL trigger should be avoided as it's an very expensive process when creating it,
While some people recommends to change in application code. For an example,
using (var db = new DbContext())
{
var result = db.Info.SingleOrDefault(x => x.InfoID == infoID);
if (result != null)
{
result.Info = "Some new value";
result.LastUpdatedBy = userID;
result.LastUpdatedDate = DateTime.UtcNow;
db.SaveChanges();
}
}
need to change and become like this.
using (var db = new DbContext())
{
var result = db.Info.SingleOrDefault(x => x.InfoID == infoID);
if (result != null)
{
result.Info = "Some new value";
result.LastUpdatedBy = userID;
result.LastUpdatedDate = DateTime.UtcNow;
var person = db.Person.SingleOrDefault(x => x.PersonID == result.PersonID);
if (person != null)
{
person.LastUpdatedBy = result.LastUpdatedBy;
person.LastUpdatedDate = result.LastUpdatedDate;
}
db.SaveChanges();
}
}
in reality, the application code is massive, a lot of code modification need to be made.
Assume there are 30+ tables, and each of them contain at least 100k of records. If creating of triggers are possible, it will be as the following:
CREATE TRIGGER TriggerName ON dbo.Info
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Person
SET LastUpdatedBy = INSERTED.LastUpdatedBy ,
LastUpdatedDate = INSERTED.LastUpdatedDate
FROM INSERTED
WHERE dbo.Person.PersonID = INSERTED.PersonID
END
GO
Is the SQL trigger should really be avoided in this scenario? Please explain based on your answer if can. Any alternative solution is welcome, performance first.
Trigger is optimal (from a performance perspective) here; it's simply like running an update statement on a bunch of rows from the front end code. I don't see why you think there is a performance penalty. Your trigger code should look more like this though:
CREATE TRIGGER TriggerName ON dbo.Info
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Person
SET LastUpdatedBy = INSERTED.LastUpdatedBy ,
LastUpdatedDate = INSERTED.LastUpdatedDate
FROM dbo.Person
INNER JOIN
INSERTED
ON dbo.Person.PersonID = INSERTED.PersonID
END
GO
There are other ways, such as making a Stored procedure that updates all tables in a transaction, or updating front end data access layer (if your front end has a lot to update, it implies it is structured wrong: one place should have responsibility for writing to this table. If your front end code has update statements peppered all through it, well.. that's a bad design) so a dedicated class maintains these two tables properly..
Right now I'd say a trigger is your easiest way out of the problem.. they aren't well liked, though not because of performance, but because they start to add confusing consequences.. imagine you as a c# developer with limited database experience, didn't know what a trigger was, and you're complaining "every time I update just this one table, all these other 27 tables change by magic! What's going on? Am I going crazy or what?" - triggers break rules like "keep all your data updating code in one place" and this is why people who engineer systems where specific parts have specific jobs, don't like them

C# linq-sql checking for null string

In my database table I have 54 rows, of those 53 are NULL in the Code column which is defined as a varchar(100). (I confirmed that those fields are null by performing queries directly on the database using my Microsoft SQL Management Studio) I have tried to use the following LINQ code to return all of the rows where the field is null:
public IQueryable<LocationHeader> LocationHeaders
{
return from p in Context.LocationTypes
where p.Code == null
select new LocationHeader
{
ID = p.ID,
Description = p.Description,
Code = p.Code
};
}
I have also found that if Iremove the where clause and then call ToList() on my LocationHeaders property and then query against it, it returns only the 53 rows I expect.
var test = LocationHeaders.ToList().Where(x => x.Code == null);
I've also tried p.Code.Equals(null) and object.Equals(p.Code, null) as suggested in related questions on this website and elsewhere. It always return an empty collection. If I change it to p.Code != null it returns all 54 rows (53 of which have a null Code column, and 1 which does not) but I view those objects the Code property has been set to null.
I also tried to null coalesce my code property into an empty string that I could check later:
Code = p.Code ?? string.Empty
But that changed exactly nothing, when I viewed the items after the query was performed, the Code property of my objects was still set to null.
Does anyone know why this might be and what I can do to fix it? I am using EF 6 Code-First, if that matters.
EDIT: I've permanently changed my code to read this way:
public IQueryable<LocationHeader> LocationHeaders
{
return from p in Context.LocationTypes
where object.Equals(p.Code, null)
select new LocationHeader
{
ID = p.ID,
Description = p.Description,
Code = p.Code
};
}
and I finally thought to check the query using SQL Server profiler. It's STILL writing the query like this:
exec sp_executesql N'SELECT
[Extent1].[ID] AS [ID],
[Extent1].[Description] AS [Description],
[Extent1].[Code] AS [Code]
FROM [dbo].[LocationType] AS [Extent1]
WHERE [Extent1].[Code] = #p__linq__0',N'#p__linq__0 nvarchar(4000)',#p__linq__0=NULL
Update your entity models to not allow NULL values in the first place... you will have to set all the fields which are currently NULL to an empty string prior to attempting this, (you can set them through SSMS)...
After you've set all the null values to an empty string.
Add this attribute to the Code property.
[Required(AllowEmptyStrings = true)]
public string Code { get; set; }
And migrate those changes over.
From here on you can just do Foo.Code == string.Empty

Solution for INSERT or UPDATE and fetching newly created id on SQL server

Is it possible to perform an insert or update with the following constraints :
Dapper is used in the project
The Primary Key is auto-incrementing positive
The newly inserted data may not have a PK value (or has a value of 0)
The data needs to have the PK value on a newly inserted row.
The query is being generated procedurally, and generalizing it would be preferable
Something like :
int newId = db.QueryValue<int>( <<insert or update>>, someData );
I have read about different solutions, and the best solution seems to be this one :
merge tablename as target
using (values ('new value', 'different value'))
as source (field1, field2)
on target.idfield = 7
when matched then
update
set field1 = source.field1,
field2 = source.field2,
...
when not matched then
insert ( idfield, field1, field2, ... )
values ( 7, source.field1, source.field2, ... )
but
it seems to fail on the third constraint and
it does not guarantee to return the newly generated id.
Because of the 5th constraint (or preferance), a stored procedure seems overly complicated.
What are the possible solutions? Thanks!
If your table has an auto-increment field, you can't assign a value to that field when inserting a record. OK you can, but it's normally a bad idea :)
Using the T-SQL MERGE statement you can put all of the values into the source table, including your default invalid identity value, then write the insert clause as:
when not matched then
insert (field1, field2, ...)
values (source.field1, source.field2, ...)
: and use the output clause to get the inserted identity value:
OUTPUT inserted.idfield
That said, I think you might be complicating your SQL code generation a little, especially for tables with a lot of fields. It is often better to generate distinct UPDATE and INSERT queries... especially if you've got some way of tracking the changes to the object so that you can only update the changed fields.
Assuming you're working on MS SQL, you can use SCOPE_IDENTITY() function after the INSERT statement to get the value of the identity field for the record in a composite statement:
INSERT INTO tablename(field1, field2, ...)
VALUES('field1value', 'field2value', ...);
SELECT CAST(SCOPE_IDENTITY() AS INT) ident;
When you execute this SQL statement you'll get back a resultset with the inserted identity in a single column. Your db.QueryValue<int> call will then return the value you're after.
For standard integer auto-increment fields the above is fine. For other field types, or for a more general case, try casting SCOPE_IDENTITY() result to VARCHAR(MAX) and parse the resultant string value to whichever type your identity column expects - GUID, etc.
In the general case, try this in your db class:
public string InsertWithID(string insertQuery, params object[] parms)
{
string query = insertQuery + "\nSELECT CAST(SCOPE_IDENTITY() AS VARCHAR(MAX)) ident;\n";
return this.QueryValue<string>(insertQuery, parms);
}
And/or:
public int InsertWithIntID(string insertQuery, params object[] parms)
{
string query = insertQuery + "\nSELECT CAST(SCOPE_IDENTITY() AS INT) ident;\n";
return this.QueryValue<int>(query, parms);
}
That way you can just prepare your insert query and call the appropriate InsertWithID method to get the resultant identity value. That should satisfy your 5th constraint with luck :)

Entity framework generates values for NOT NULL columns which has default defined in db

Hi I have a table Customer. One of the columns in table is DateCreated. This column is NOT NULL but default values is defined for this column in db.
When I add new Customer using EF4 from my code.
var customer = new Customer();
customer.CustomerName = "Hello";
customer.Email = "hello#ello.com";
// Watch out commented out.
//customer.DateCreated = DateTime.Now;
context.AddToCustomers(customer);
context.SaveChanges();
Above code generates following query.
exec sp_executesql N'insert [dbo].[Customers]([CustomerName],
[Email], [Phone], [DateCreated], [DateUpdated])
values (#0, #1, null, #2, null)
select [CustomerId]
from [dbo].[Customers]
where ##ROWCOUNT > 0 and [CustomerId] = scope_identity()
',N'#0 varchar(100),#1 varchar(100),#2 datetime2(7)
',#0='Hello',#1='hello#ello.com',#2='0001-01-01 00:00:00'
And throws following error
The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.
The statement has been terminated.
Can you please tell me how NOT NULL columns which has default values at db level should not have values generated by EF?
DB:
DateCreated DATETIME NOT NULL
DateCreated Properties in EF:
Nullable: False
Getter/Setter: public
Type: DateTime
DefaultValue: None
Thanks.
From my knowledge of EF (which is minimal), it does not grab the default value from the schema. The fact that you are inserting the row and the column is marked as NOT NULL, means EF thinks it should be inserting a value for that column which happens to have the value of DateTime.MinValue.
You may need to force your own values in the entities constructor or create some factory methods.
Is there anything on the property pages of the table designer in EF that lets you specify a default value?
You have to said to the ORM that the property is generated by the database. For the example in the Customer Class in the previous example, the following code should work.
public class Customer{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]//The database generates a value when a row is inserted.
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] //The database generates a value when a row is inserted or updated.
public DateTime DateCreated { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.IComputed)]//The database generates a value when a row is inserted or updated.
public DateTime DateUpdated { get; set; }
}
Don't forget to add the
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
to the primary key, for a reason that i not know, when you use the annotation of DatabaseGeneratedAttribute at least once in the Model Class, you have to specify the the generated option method. When i use them, i have an error because the ORM was trying to insert the key in the query statement instead of let the database to generate them.
There are three Database Generated Options
Computed : The database generates a value when a row is inserted or updated.
Identity : The database generates a value when a row is inserted.
None : The database does not generate values.
You can find the documentation here Database Generated Options
NOT NULL column means that property is not nullable and it must have a value in your entity. If you do not specify value in value type like DateTime the default value will be persisted. Default value for DateTime is DateTime.MinValue.
As I know EF doesn't support scenario where you can use NOT NULL column and not send any value from your application. The only exception is when you set StoreGeneratedPattern for the property to Identity or Computed but in such case you will not be able to set the value in the application.
So you can set value in application or in database. Not in both.
Possibly the DateCreated column is a smalldatetime and needs to be a datetime type? Try changing the column type in Visual Studio, or Management Studio.
Set the DateCreated allow null
Add a trigger for INSERT
CREATE TRIGGER [dbo].[ItemCreated]
ON [dbo].[myTable]
AFTER INSERT
AS
UPDATE [dbo].[myTable]
SET [DateCreated ] = GETDATE()
WHERE [ID] IN (SELECT DISTINCT [ID] FROM [Inserted]) AND [DateCreated] is NULL

Categories

Resources