I have a simple VS 2012 C# form with a data grid control bound to a binding source. The SQL Server data source that this is bound to has an identity column (ID) that is also the primary key.
In the data set properties for this ID field, I have AutoIncrement = true, AutoIncrement Seed = -1 (default) and AutoIncrementStep = -1 (default)
I cannot save a new row to the database.
If I add a new record using the tool strip, clear the ID field, then click the save icon I get this exception: “Cannot insert explicit value for identity column in table xxxx when IDENTITY INSERT is set to off”. If I leave the auto generated value of “-1” in the field, I get the same exception.
The code attached to the save button:
private void tblClassificationBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
this.tblClassificationBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.exportComplianceSISVTDataSet);
}
Executing the last step results in the exception.
Some forum respondents recommend setting IDENTITY INSERT to “ON” as a workaround but many advise against this.
I believe you are binding the data from Sql server table. Set the IDENTITY property in the SQL Server Engine itself.That is on your table in the Server.
Set the Seed to 1 and Increment to 1.
More on Identity: Click here
SQL Fiddle Demo
I had exactly this problem. Setting Identity_Insert to ON for the table did not change the error, and created inconsistent behaviour in SQL Server Management Studio. When inserting records with the identity field defined, records were apparently inserted successfully but could not be seen in results from a select statement.
I solved the problem by changing the Update and Insert statements defined in the InitAdapter function in DataSetDesigner.cs to exclude the Identity field. An example is shown below, where ID is the Identity field for the Update command.
//this._adapter.UpdateCommand.CommandText = #"UPDATE [dbo].[Stopwords] SET [ID] = #ID, [Stopword] = #Stopword, [DateAdded] = #DateAdded WHERE (([ID] = #Original_ID) AND ([Stopword] = #Original_Stopword) AND ((#IsNull_DateAdded = 1 AND [DateAdded] IS NULL) OR ([DateAdded] = #Original_DateAdded)));
//SELECT ID, Stopword, DateAdded FROM Stopwords WHERE (Stopword = #Stopword)";
this._adapter.UpdateCommand.CommandText = #"UPDATE [dbo].[Stopwords] SET [Stopword] = #Stopword, [DateAdded] = #DateAdded WHERE (([ID] = #Original_ID) AND ([Stopword] = #Original_Stopword) AND ((#IsNull_DateAdded = 1 AND [DateAdded] IS NULL) OR ([DateAdded] = #Original_DateAdded)));
SELECT ID, Stopword, DateAdded FROM Stopwords WHERE (Stopword = #Stopword)";
As these changes are in autogenerated code, they will need to be reapplied if the dataset code is regenerated.
Related
I have a table Estimation which has an column EstimationNo,i am trying to get the max EstimationNo like this-
var result = cont.SalesEstimateCont.Where(x => x.Org_ID == CurrentOrgId);
var estimationMaxNo = result.Any() ? result.Max(x => x.EstimateNo) + 1 : 1;
var DigitalEstimate = new SalesEstimate()
{
EstimateNo=estimationMaxNo;
};
cont.Estimate.Add(DigitalEstimate );
cont.Savechanges();
but the problem is, if same table is saving by different users at same time its saving the same EstimationNo for both users. like- 10,10
Now, how to handle this issue..please give some solution.
Best strategy is to let db engine (I assume that it is SQL Server) handle incrementing of EstimateNo field. This can be done with identity specification which can be added to normal not primary key field also.
ALTER TABLE SalesEstimateCont drop column EstimateNo
go
ALTER TABLE SalesEstimateContadd Add EstimateNo int NOT NULL IDENTITY (1,1)
Please note: if you have existing data or some data should be modified, you may need some extra effort to achieve this (i.e with temp tables and by setting IDENTITY INSERT ON)
I got a simple answer.I just had to use transacationScope class.
and lock the resource table. like this-
using (TransactionScope scope = new TransactionScope())
{
cont.Database.ExecuteSqlCommand("SELECT TOP 1 * FROM Sales__Estimate WITH (TABLOCKX, HOLDLOCK)");
var result = cont.SalesEstimateCont.Where(x => x.Org_ID == CurrentOrgId);
var estimationMaxNo = result.Any() ? result.Max(x => x.EstimateNo) + 1 : 1;
var DigitalEstimate = new SalesEstimate()
{
EstimateNo=estimationMaxNo;
};
cont.Estimate.Add(DigitalEstimate );
cont.Savechanges();
}
If you can make EstimateNo an Identity column, that is the easiest/best way to fix this. If you can change this to a Guid, that would be another easy way to fix this as PK would be unique regardless of the user.
If you can't do either of these and you must take Max() manually, you might want to consider creating another table that stores the next available number there. Then you can create a new SqlCommnand with a Serializable transaction to lock the table, update the # by 1 and select it back. If two update commands hit at the same time, only one update will run and won't let go until that connection with Serializable transaction gets closed. This allows you to select the newly updated number before the other update runs and get the now "unique" next number.
You can OrderByDescending and then Take the the first record
var estimationMaxNo = result.OrderByDescending(x => x.EstimateNo).Take(1);
It can be done in a single command. You need to set the IDENTITY property for primary id
ALTER TABLE SalesEstimateCont ADD Org_ID int NOT NULL IDENTITY (1,1) PRIMARY KEY
I want to update a column values in sql server table after copying those value from some other table. i.e.
where I have tested this query but not succeeded
update subset_aminer.dbo.sub_aminer_paper
set sub_aminer_paper.p_abstract =
(select aminer_paper.p_abstract
from aminer.dbo.aminer_paper
where pid IN (86257, 116497, 119248, 135555, 147554,
149720, 173254, 191333, 196650, 196656,
.....long list of other values ......
1727408, 1737809, 2034956)
)
I have to copy data from other database table to my destination database table. So that's y using subset_aminer.dbo.subset_aminer_paper.p_abstract and aminer.dbo.aminer_paper.p_abstract
Please help with acknowledgements. Thanks
update subset_aminer.dbo.sub_aminer_paper SI
set SI.p_abstract = AP.p_abstract
from aminer.dbo.aminer_paper AP
where AP.pid IN (86257, 116497, 119248, 135555, 147554,
149720, 173254, 191333, 196650, 196656,
.....long list of other values ......
1727408, 1737809, 2034956) AND AP.ID = SI.ID
Just a suggestion,
UPDATE subset_aminer.dbo.sub_aminer_paper
SET p_abstract=aminer.dbo.aminer_paper.p_abstract
FROM aminer.dbo.aminer_paper
WHERE subset_aminer.dbo.sub_aminer_paper.pid IN (86257, 116497, 119248, 135555, 147554,
149720, 173254, 191333, 196650, 196656,
.....long list of other values ......
1727408, 1737809, 2034956)
With C# and MySQL ADO.NET connector, I use MySqlDataAdapter and MySqlCommandBuilder to update a table.
Is it possible to temporarily force an autoinc field ?
DataRow row = ...
row["Id"] = 90; // force autoinc value
using (MySqlDataAdapter adapter = ...)
{
MySqlCommandBuilder builder = new MySqlCommandBuilder(adapter);
int result = adapter.Update(new DataRow[] { row });
// The inserted ID is 20 and not 90...
Yes you can, so long as you explicitly specify the value as in the code example that you provided, it will work. Off course it will fail if a duplicate value is specified.
The auto increment is only applied to an insert if you omitted the value or the value is NULL or 0 or DEFAULT.
#A.Baudouin I think you meant to say "Unfortunately, as you can see in my example above, it does not work"! Did you ever find a solution? I had exactly the same problem but found that it works OK if you use MySql Workbench to modify the value. All the Workbench does is a normal UPDATE command, so I am guessing this is a problem of MySqlAdapter, or maybe a "feature"! I ended up updating the database directly like:
MySqlHelper.ExecuteNonQuery(myConnection, "UPDATE MyTable SET Id = 90 WHERE Id = 7", null);
(or however you want to identify the correct row with WHERE). This works OK.
I am inserting a list of records to the DB table with Linq to sql like this:
//my DataContext Class
using (VTMMedicalDBDataContext objVTMMedicalDBDataContext = new VTMMedicalDBDataContext())
{
ReadOnlyCollection<TimeZoneInfo> objTimeZones = null;
objTimeZones = TimeZoneInfo.GetSystemTimeZones();
if (objTimeZones.Count > 0)
{
//List<TimeZoneMaster> listTimeZones = new List<TimeZoneMaster>();
TimeZoneMaster objTimeZoneMaster = new TimeZoneMaster();
foreach (var timezone in objTimeZones.ToList())
{
objTimeZoneMaster.TimeZoneName = timezone.DisplayName;
var localName = timezone.DisplayName;
objTimeZoneMaster.TimeZoneOffsetInMinutes = Convert.ToInt32(timezone.BaseUtcOffset.TotalMinutes);
objVTMMedicalDBDataContext.TimeZoneMasters.InsertOnSubmit(objTimeZoneMaster);
objVTMMedicalDBDataContext.SubmitChanges();
}
}
}
I have a primary key but I have already made it as AutoGeneratd as true in DBML Nullable as false.Still I am not able to get rid of it...Please suggest some way around.
Finally After Much hassle I got my answer with a new concept..
For all those facing similar problems,here is a clear solution:
You need to create the object of the 'Table' class(which object you want to update in DB),
inside the loop(*for each,for or any other..)so that same record is not updated and each object uses a different memory location(as the local instance gets destroyed inside the loop only..)*
I have a primary key but I already made it as AutoGenerated as true in DBML
That's fine, however, have you made the field auto-generated at database level? AutoGenerated at DBML level won't actually generate any values for you, it just indicates to the model that the value will be generated by the storage provider (so it probably omits sending it as part of the query).
You have to update table to have that column with identity value (autoincrement).
Then update your DBML model-> Delete the table from DBML designer, refresh your table at server explorer and drag and drop the table object again. Generate the project and it should work nice.
As an alternative you can call stored procedure inside db which will do the insert:
Updating database by using stored procedures.
using (VTMMedicalDBDataContext objVTMMedicalDBDataContext = new VTMMedicalDBDataContext())
{
ReadOnlyCollection objTimeZones = null;
objTimeZones = TimeZoneInfo.GetSystemTimeZones();
if (objTimeZones.Count > 0)
{
//List<TimeZoneMaster> listTimeZones = new List<TimeZoneMaster>();
foreach (var timezone in objTimeZones.ToList())
{
TimeZoneMaster objTimeZoneMaster = new TimeZoneMaster();
objTimeZoneMaster.TimeZoneName = timezone.DisplayName;
var localName = timezone.DisplayName;
objTimeZoneMaster.TimeZoneOffsetInMinutes = Convert.ToInt32(timezone.BaseUtcOffset.TotalMinutes);
objVTMMedicalDBDataContext.TimeZoneMasters.InsertOnSubmit(objTimeZoneMaster);
objVTMMedicalDBDataContext.SubmitChanges();
}
}
}
I have a winforms application with two DataGridViews displaying a master-detail relationship from my Person and Address tables. Person table has a PersonID field that is auto-incrementing primary key. Address has a PersonID field that is the FK.
I fill my DataTables with DataAdapter and set Person.PersonID column's AutoIncrement=true and AutoIncrementStep=-1. I can insert records in the Person DataTable from the DataGridView. The PersonID column displays unique negative values for PersonID. I update the database by calling DataAdapter.Update(PersonTable) and the negative PersonIDs are converted to positive unique values automatically by SQL Server.
Here's the rub. The Address DataGridView show the address table which has a DataRelation to Person by PersonID. Inserted Person records have the temporary negative PersonID. I can now insert records into Address via DataGridView and Address.PersonID is set to the negative value from the DataRelation mapping. I call Adapter.Update(AddressTable) and the negative PersonIDs go into the Address table breaking the relationship.
How do you guys handle primary/foreign keys using DataTables and master-detail DataGridViews?
Thanks!
Steve
EDIT:
After more googling, I found that SqlDataAdapter.RowUpdated event gives me what I need. I create a new command to query the last id inserted by using ##IDENTITY. It works pretty well. The DataRelation updates the Address.PersonID field for me so it's required to Update the Person table first then update the Address table. All the new records insert properly with correct ids in place!
Adapter = new SqlDataAdapter(cmd);
Adapter.RowUpdated += (s, e) =>
{
if (e.StatementType != StatementType.Insert) return;
//set the id for the inserted record
SqlCommand c = e.Command.Connection.CreateCommand();
c.CommandText = "select ##IDENTITY id";
e.Row[0] = Convert.ToInt32( c.ExecuteScalar() );
};
Adapter.Fill(this);
SqlCommandBuilder sb = new SqlCommandBuilder(Adapter);
sb.GetDeleteCommand();
sb.GetUpdateCommand();
sb.GetInsertCommand();
this.Columns[0].AutoIncrement = true;
this.Columns[0].AutoIncrementSeed = -1;
this.Columns[0].AutoIncrementStep = -1;
You need to double click the relationship in the dataset designer, and select Cascade Updates. When your real SQL server generated PK values for your Person table are generated, it will automatically set the foreign key values in the address table as well.
You don't need to do any of that RowUpdated event stuff. Its built into the dataset functionality.
I had a similar problem, but my solution was a little different.
#Noel Kennedy: Your solution does not work with SQL Server 2005 CE, because it doesn't support multiple statements and the TableAdapter won't generate the refresh code needed to update the autoincrement columns in the parent table.
NOTE: You still need Cascade Updates in the relationship so the child tables get updated.
I also add a method in my TableAdapter, which is generic enough to just copy/paste in all your parent TableAdapters. The only thing that I change is the identity row type and index (if needed). I also add a query to the TableAdapter called GetIdentity(). You can add it to the TableAdapter in the dataset designer by adding a scalar query with sql="SELECT ##IDENTITY;"
Now the custom function is:
public int InsertAndRefresh(System.Data.DataTable dataTable)
{
int updated = 0;
System.Data.DataRow[] updatedRows = dataTable.Select("", "", System.Data.DataViewRowState.Added);
bool closed = (this.Connection.State == System.Data.ConnectionState.Closed);
if (closed)
this.Connection.Open();
foreach (System.Data.DataRow row in updatedRows)
{
updated+=this.Adapter.Update(new global::System.Data.DataRow[] { row });
decimal identity = (decimal)this.GetIdentity();
row[0] = System.Decimal.ToInt64(identity);
row.AcceptChanges();
}
if (closed)
this.Connection.Close();
return updated;
}
You want to call this on the parent first. Then do everything as usual (update parent and then children).
Cheers!