I want to enter unique index value into my database when insert other table data. In this case my table index column ID is unique but not auto-increment.
I also want to check the maximum of index value in target table. If the table is empty then index value starts from 0, OR if it contains 1 row then the index value starts from 2.
Already I can successfully do this operation using a stored procedure. But I want to do this operation using Entity Framework when saving data.
My stored procedure is:
ALTER PROCEDURE [dbo].[CreatePerson]
#Name Nvarchar(50),
#Code Nvarchar(50)
AS
BEGIN
Declare #Id int
SET #Id = ISNULL(((SELECT MAX([id]) FROM dbo.tbl_Person)+1),'1')
Insert Into dbo.tbl_Person([Id], [Name], [Code])
Values (#id, #Name, #Code)
END
Can anyone tell me what the LINQ Command for this index value save process?
A straight translation of your SQL stored procedure into Linq would basically be something like this:
var newId = ctx.Persons.Any() ? ctx.Persons.Select(p => p.Id).Max() + 1 : 1;
var newPerson = new Person { Id = newId, Name = someName, Code = someCode };
ctx.Persons.Add(newPerson);
ctx.SaveChanges();
Note: this solution is certainly NOT recommended for inserting unique id's in a database, consider carefully the suggestions of Sharped and Magnus in the comments below the question to better use auto-increment or random guids to solve your problem.
With an auto-incremented id column your code would look like this:
var newPerson = new Person { Name = someName, Code = someCode };
ctx.Persons.Add(newPerson);
ctx.SaveChanges();
var newId = newPerson.Id;
Note that EF will update the id column of the newly created entity for you in code automagically.
Related
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.
So I have this:
INSERT INTO Track (Trackname, Trackinfo) VALUES (Value1, Value2)
INSERT INTO Artiest (Artist) VALUES (Value1)
Both of these tables have an ID that is auto_increment
how can I create a table that will have both the IDs??
I am trying to do this in C#
EDIT: Maybe there is a way in MySQL where I can get the last IDs of the 2 tables?
You either write a stored procedure using T-SQL and SCOPE_IDENTITY().
Or you use LINQ to Entities and retrieve the ID of the newly created Entity.
T-SQL:
DECLARE #TrackID AS int
INSERT INTO Track (Trackname, Trackinfo) VALUES (Value1, Value2)
SET #TrackID = SCOPE_IDENTITY()
DECLARE #ArtiestID AS int
INSERT INTO Artiest (Artist) VALUES (Value1)
SET #ArtiestID = SCOPE_IDENTITY()
Now use those ID's to Insert into your table.
INSERT INTO TrackArtiest (TrackID , ArtiestID ) VALUES (#TrackID , #ArtiestID)
When using Linq to Entities:
var myTrackObject = new Track(Value1, Value2);
var myArtiestObject= new Artiest(Value1);
// db is your datacontext
db.Tracks.Insert(myTrackObject);
db.Artiests.Insert(myArtiestObject);
db.SubmitChanges();
// You can retrieve the id from the object
int trackID = myTrackObject.ID;
int artiestID = myArtiestObject.ID;
var myTrackArtiest = new TrackArtiest(trackID, artiestID);
db.TrackArtiest.Insert(myTrackArtiest );
db.SubmitChanges();
Is this what you meant ?
If you are using SQL and want to return the ID of the insert operation. Use: SCOPE_IDENTITY() function in SQL. But you have to return it and map it to C#
In a c# desktop application I am getting this list of data which I am reading by barcode into text file; here is the result;
R900, 27674T07, 27438T17, 27736T21, 26609T08,
R901, 27770T12, 27833T07, 26402T12, 27771T09, 26003T13,
R902, 26003T14, 26402T11, 26246T17,
R904, 28055T09, 25356T08, 25825T07, 25556T09,
and I am transforming it to update queries;
UPDATE TABLE SET NUMBER = R900 WHERE id in ( 27674T07, 27438T17, 27736T21, 26609T08)
UPDATE TABLE SET NUMBER = R901 WHERE id in ( 27770T12, 27833T07, **26402T12**, **27771T09**, 26003T13)
UPDATE TABLE SET NUMBER = R902 WHERE id in ( 26003T14, **26402T11**, 26246T17)
UPDATE TABLE SET NUMBER = R904 WHERE id in ( 28055T09, 25356T08, 25825T07, **25556T09**)
Finally I am executing this SQL query. But the problem is I don't know which id is not found in IN clause in database. I need to report back to user which id didn't found with its NUMBER
For example the bold id's are not found in database, and couldn't update. So expected result is:
NUMBER id
R901 26402T12
R901 27771T09
R902 26402T11
R903 25556T09
how can I return this?
You could do something like this
declare #mytable as TABLE
(
Id nvarchar(20)
)
UPDATE TABLE SET NUMBER = R900
OUTPUT INSERTED.Id into #mytable
WHERE id in ( 27674T07, 27438T17, 27736T21, 26609T08)
Select * from #mytable
#mytable will contain updated Ids only.
Hope this helps.
create a temp table to store the splitted value into it.
then
SELECT temp.number, temp.Id
FROM #temp temp
LEFT OUTER JOIN TABLE ON temp.id = TABLE.id
WHERE TABLE.id is null
I am making a form in asp using C# and my task is to make a course and packages detail and insert the , i have make three tables one is Course which contain Id(pk) and Course-name, packages table contain Id(pk) and pkg-name and other table is Pkg-detail which contain Pkg-id(pk) and it is foreign key for Id in packages table and other attribute is course-id it is foreign key for course(ID) and my task is when i select pkg-name from drop down and course from check box from asp form that particular Id for pkg-name and Id for Course-name will insert in third table pkg-detail as i am new i have searched lot but cannot find related answer.
SqlCommand cmm = new SqlCommand("Pkc", con);
cmm.CommandType = CommandType.StoredProcedure;
cmm.Parameters.AddWithValue("#pname", drop1.SelectedValue);
cmm.Parameters.AddWithValue("#course", check1.SelectedValue);
con.Open();
cmm.ExecuteNonQuery();
con.Close();
CREATE PROCEDURE [dbo].[Pkc]
#course nvarchar(50),
#pname nvarchar(50)
AS
begin
insert into Pkg(CourseId)values((select Id from Courses where CourseName=#course));
insert into Pkg(PkgId) values ((select Id from PKgCode where PkgName=#pname))
end
You didn't mentioned your problem, but from your text it seems that you want to pass courceID and PackageID into your StoredProc, your code has a problem, since dropdown.SelectedValue is string by AddWithValue you will add two string parameter while I think they should be integer. So cast them as int or use other method that accepts type of parameter.
Edit
Base on comments it seems that your procedure should be like this:
CREATE PROCEDURE [dbo].[Pkc] #course nvarchar(50), #pname nvarchar(50)
AS
begin
declare #courceID int
declare #pkgID int
select #courceID = Id from Courses where CourseName=#course
select #pkgID = Id from PKgCode where PkgName=#pname
insert into Pkg-Detail(CourseId,PkgId) values(#courceID, #pkgID)
end
I have a table Student. This table has a column ID which is auto-increment. I want to write a stored procedure to add a new student and return the ID. I'm newbie at this.
This is my code. Please check if it's wrong and fix for me and show me how to code to use it in C#. I used Entity Framework 4.
#Name NVARCHAR(50),
#Birthday DATETIME,
#ID bigint OUTPUT
AS
BEGIN
SET XACT_ABORT ON
BEGIN
INSERT INTO [Student](Name, Birthday) VALUES (#Name, #Birthday);
SELECT #ID = SCOPE_IDENTITY()
END
END
It is better you can C# code instead SP when your working with EF4.
Using Entity Framework, this is all done automagically for you.
using (MyEntities context = new MyEntities())
{
var student = new Student()
{
Name = "some value",
Birthday = "some Birthday"
};
context.Students.AddObject(student);
context.SaveChanges();
int ID = student.ID; // You will get here the Auto-Incremented table ID value.
}
Saving in Entity Framework will automatically update the "auto-increment" ID column. You just read it out after the call to .SaveChanges();
EDIT:
Also read this post if you encounter any issues getting the "auto-increment" ID value.
#Name NVARCHAR(50),
#Birthday DATETIME,
#ID bigint OUTPUT
AS
BEGIN
SET XACT_ABORT ON
BEGIN
INSERT INTO [Student](Name,Birthday)
VALUES(#Name,#Birthday);
SELECT #ID = SCOPE_IDENTITY()
END
I just added commas in between fields
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx
This should provide you all the information you need to build a C# application calling your Stored Procedure.