How do I execute a query using LINQ to SQL? a query that something goes like this.
Let's say I have this table
CREATE TABLE dbo.Students
(
StudentID INT IDENTITY(1,1) PRIMARY KEY,
Name SYSNAME
);
CREATE TABLE dbo.StudentLoans
(
LoanID INT IDENTITY(1,1) PRIMARY KEY,
StudentID INT FOREIGN KEY REFERENCES dbo.Students(StudentID),
Amount BIGINT -- just being funny
);
Then I wanted to execute this query.
DECLARE
#Name SYSNAME = N'user962206',
#LoanAmount BIGINT = 50000,
#StudentID INT;
INSERT dbo.Students(Name)
SELECT #Name;
SELECT #StudentID = SCOPE_IDENTITY();
INSERT dbo.StudentLoans(StudentID, Amount)
SELECT #StudentID, #LoanAmount;
Is that possible? even if your Rows and Columns are mapped? how can I execute that query with LINQ to SQL ?
It's been a while, but wouldn't it be something like this?
Assuming you've dragged all your tables onto the Linq2Sql designer, simply create a Student object, add a StudentLoan to its StudentLoans collection, and add the Student to the Context.Students collection with myContextInstance.Students.InsertOnSubmit(newStudent) and write out the changes with a call to myContextInstance.SubmitChanges.
So, putting it all together:
using(var myContextInstance=new YourContext())
{
var student = new Student(){Name = "user962206"};
var studentLoan = new StudentLoan(){Amount = 50000};
student.StudentLoans.Add(studentLoan);
myContextInstance.Students.InsertOnSubmit(student);
myContextInstance.SubmitChanges();
}
The code snippet works if your DataContext looks like this:
This is the result of just dragging your tables to the design surface of the dbml file.
If by your question you mean "how do a execute a raw SQL query with LINQ to SQL," then look at the ExecuteCommand and ExecuteQuery methods:
ExecuteCommand -
http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executecommand.aspx
ExecuteQuery (Overload 1) - http://msdn.microsoft.com/en-us/library/bb534292.aspx
ExecuteQuery (Overload 2) - http://msdn.microsoft.com/en-us/library/bb361109.aspx
All these methods take a raw SQL query, like yours, and runs it against the database
If you meant by your question "how do I write this SQL query as a LINQ query" then please clarify your question.
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#
I looked around for a similar question (I'm sure there is one somewhere) but could not find one.
I have a list of IDs that for each of the IDs I need to update another column of that IDs row to the same string.
Essentially, I want something like this:
List<int> uniqueIDs;
UPDATE my_table
SET certainColumn = "foo bar"
WHERE uniqueID = uniqueIDs[0]
OR uniqueID = uniqueIDs[1]
...
OR uniqueID = uniqueID[uniqueIDs.Length-1]
I know this could be achieved by surrounding this in a for/foreach-loop, but I was wondering if there is a better way to get this done, possibly in one database connection?
Any help is greatly appreciated.
Well, you could use a TVP. First, create this on your server:
CREATE TYPE dbo.UniqueIDs AS TABLE(ID INT PRIMARY KEY);
Then a stored procedure:
CREATE PROCEDURE dbo.UpdateByID
#tvp dbo.UniqueIDs READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET certainColumn = 'foo bar'
FROM dbo.my_table AS t
INNER JOIN #tvp AS tvp
ON t.uniqueID = tvp.ID;
END
Or:
CREATE PROCEDURE dbo.UpdateByID
#tvp dbo.UniqueIDs READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET certainColumn = 'foo bar'
FROM dbo.my_table AS t
WHERE EXISTS (SELECT 1 FROM #tvp
WHERE ID = t.uniqueID);
END
Then instead of a List use a DataTable to hold your IDs in your C# application, and call the stored procedure, passing #tvp as a Structured parameter. I have simple examples of the C# side posted all over this site:
How to pass an array into a SQL Server stored procedure
Insert entire DataTable into database at once instead of row by row?
Parameters to the EXISTS clause in a stored procedure
var query = "UPDATE my_table
SET certainColumn = 'foo bar'
WHERE uniqueID in (" + String.Join(",", uniqueIDs) + ")"
I guess you could do something like this. Get your List of Ids in some TempTable or table variable and use IN operator in your update statement. something like this .....
UPDATE my_table
SET certainColumn = 'foo bar'
WHERE uniqueID IN (SELECT uniqueID
FROM #List_Table)
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 :)
I'm using EF4 with Visual Studio 2010 and SQL Server 2008 R2.
I am selecting an entity. After that I call a stored procedure which updates that entity en reselects it from the database. When I catch the result of the stored procedure in code, I see that the old (previously selected) properties.
Obviously I'm looking at the cached entity value. Is there a way to signal EF that my entity was updated? Or a magic property of some sort?
My database table (and entity) look something like this:
CREATE TABLE [Message]
(
ID int IDENTITY(1, 1) PRIMARY KEY,
Content XML,
StateID int NOT NULL,
)
My SP is something like this:
CREATE PROCEDURE sp_Queue_s
AS
BEGIN
DECLARE #queue table ([ID] int NOT NULL)
BEGIN TRAN
INSERT INTO #queue
SELECT [ID]
FROM [Message]
WHERE StateID = 1
UPDATE [Message]
SET StateID = 2
WHERE ID IN (SELECT ID FROM #queue)
COMMIT TRAN
-- Select the queue
SELECT [ID], [Content], [Message]
FROM [Message]
WHERE [ID] IN (SELECT ID FROM #queue)
END
My C# code looks something like this:
using (var context = new MyEntities())
{
int id = 1;
var message = context.Messages.Single(m => m.ID == id);
var messages = context.GetQueue(); // Function import of sp_Queue_s, maps on the Message entity
var messageUpdated = messages.Single(m => m.ID == id);
}
GetQueue should be a generated method internally calling context.ExecuteFunction. ExecuteFunction has multiple overloads and one of them accepts MergeOption. Try to call ExecuteFunction directly with MergeOption.OverwriteChanges.