MS Access - C# - Retrieve the latest inserted guid - c#

Is there a way to retrieve the latest inserted guid in access with C#?
I tried this: Created a table Cars with a field Id of type autonumber, replicationID and a field Name varchar(250).
var command = myConnection.CreateCommand();
command.Connection.Open();
command.CommandText = "INSERT INTO Cars(Name) VALUES ('Pagani')";
command.ExecuteNonQuery();
command = context.Database.Connection.CreateCommand();
command.CommandText = "SELECT ##Identity";
Console.WriteLine(command.ExecuteScalar());
command.Connection.Close();
The issue which I am getting is:
Console.WriteLine(command.ExecuteScalar());
always shows 0
EDIT
To create the table you can use this statement over the C# OleDb connection (I think that from MS Access query does not work)
CREATE TABLE [Cars] (
[Id] guid not null DEFAULT GenGUID(),
[Name] text null
);
ALTER TABLE [Cars] ADD CONSTRAINT [PK_Cars_6515ede4] PRIMARY KEY ([Id])

I know this is not exactly what you are asking for, but let me suggest an alternative solution which might solve your underlying problem.
Create the GUID in C# and pass it to your insert:
var newGuid = Guid.NewGuid();
var command = myConnection.CreateCommand();
command.Connection.Open();
command.CommandText = "INSERT INTO Cars(Id, Name) VALUES (?, 'Pagani')";
command.Parameters.AddWithValue("#Id", newGuid); // Note: OleDb ignores the parameter name.
command.ExecuteNonQuery();
Console.WriteLine(newGuid);
GUIDs are unique. It really doesn't matter whether it is generated by your application or by the Access database driver.
This option is in all respects superior to reading the GUID afterwards:
You only need one database access.
It's less code.
It's easier.
And you can still omit the GUID in your INSERT in cases where you don't need to know the GUID - no need to change existing code.

If SELECT ##IDENTITY does not work for "ReplicationID" AutoNumber fields then the most likely way to retrieve such a value for a new record is to use an Access DAO Recordset insert, like this:
// required COM reference:
// Microsoft Office 14.0 Access Database Engine Object Library
var dbe = new Microsoft.Office.Interop.Access.Dao.DBEngine();
Microsoft.Office.Interop.Access.Dao.Database db = dbe.OpenDatabase(
#"C:\Users\Public\Database1.accdb");
Microsoft.Office.Interop.Access.Dao.Recordset rst = db.OpenRecordset(
"SELECT [Id], [Name] FROM [Cars] WHERE FALSE",
Microsoft.Office.Interop.Access.Dao.RecordsetTypeEnum.dbOpenDynaset);
rst.AddNew();
// new records are immediately assigned an AutoNumber value ...
string newReplId = rst.Fields["Id"].Value; // ... so retrieve it
// the returned string is of the form
// {guid {1D741E80-6847-4CB2-9D96-35F460AEFB19}}
// so remove the leading and trailing decorators
newReplId = newReplId.Substring(7, newReplId.Length - 9);
// add other field values as needed
rst.Fields["Name"].Value = "Pagani";
// commit the new record
rst.Update();
db.Close();
Console.WriteLine("New record added with [Id] = {0}", newReplId);
which produces
New record added with [Id] = 1D741E80-6847-4CB2-9D96-35F460AEFB19

You can try like this using the OUTPUT :
INSERT INTO myTable(myGUID)
OUTPUT INSERTED.myGUID
VALUES(GenGUID())
You can try like this:
string str1 = "INSERT INTO Cars(Name) VALUES ('Pagani')";
string str2 = "Select ##Identity";
int ID;
using (OleDbConnection conn = new OleDbConnection(connect))
{
using (OleDbCommand cmd = new OleDbCommand(str1, conn))
{
conn.Open();
cmd.ExecuteNonQuery();
cmd.CommandText = str2;
ID = (int)cmd.ExecuteScalar();
}
}

Related

Trying to get SELECT SCOPE_IDENTITY() as c# variable

I am inserting a row into one table then want to get that new ID so I can add it in another variable where I have email address stored.
var db = Database.Open("myDB");
var insertCommand1 = "INSERT INTO myDB (FirstName, LastName) Values(#0, #1)";
db.Execute(insertCommand1, first, last);
var lastInsertedId = db.QueryValue("SELECT SCOPE_IDENTITY()");
var insertCommand2 = "INSERT INTO email (id_person, email) Values(#0, #1)";
db.Execute(insertCommand2, lastInsertId, email);
where id_person is the id that is created in my first table. When I run the code I get lastInsertedId = {}. Any reason why it is not grabbing a value for id_person which is a primary key, int , not null for my first table? --Tim
From the documentation of SCOPE_IDENTITY(), emphasis mine:
Returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
Because you are using two queries they are considered two batches. You need to do your insert and your select in a single query.
I don't know what library you are using, so I am just guessing on the syntax but I beleive you need something like
var db = Database.Open("myDB");
var insertCommand1 = "INSERT INTO myDB (FirstName, LastName) Values(#0, #1); " +
"SELECT SCOPE_IDENTITY()";
var lastInsertedId = db.QueryValue(insertCommand1, first, last);
var insertCommand2 = "INSERT INTO email (id_person, email) Values(#0, #1)";
db.Execute(insertCommand2, lastInsertId, email);
It is very easy to get the SCOPE_IDENTITY() value back from SQL Server. I will give an example where I was able to print the SCOPE_IDENTITY() data back in c# label.
My Code Snippet in a Submit from data insert
btnSubmit_Click()
{
Random s = new Random();
cmd = new SqlCommand("INSERT INTO [dbo].[tblMemberAccount] ([Userid], [User_pwd], [User_mobile], [User_referal], [UserWallet]) VALUES(#Userid, #User_pwd, #User_mobile, #User_referal, #UserWallet) ​select scope_identity()", cons);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Userid",s.Next(4506,9999));
cmd.Parameters.AddWithValue("#User_pwd",txtPassword.Text);
cmd.Parameters.AddWithValue("#User_mobile",txtPhoneNumber.Text);
cmd.Parameters.AddWithValue("#User_referal",txtReferral.Text);
cmd.Parameters.AddWithValue("#UserWallet",10);
cons.Open();
int g = Convert.ToInt32(cmd.ExecuteScalar());
cons.Close();
lblConfirm.Text = "Member " +g+ " added successfully!";
}
Here the value 'g' is returning the scope_identity value.
If the database is in SQL SERVER , create a SQL parameter and set the direction to
"Output".
Please check this link :
Getting the identity of the most recently added record

SQL server - inserting a string with a single quotation mark

I iterate over an external source and get a list of strings. I then insert them into the DB using:
SqlCommand command = new SqlCommand(commandString, connection);
command.ExecuteNonQuery();
Where commandString is an insert into command. i.e.
insert into MyTable values (1, "Frog")
Sometimes the string contains ' or " or \ and the insert fails.
Is there an elegant way to solve this (i.e. #"" or similar)?
Parameters.
insert into MyTable values (#id, #name)
And
int id = 1;
string name = "Fred";
SqlCommand command = new SqlCommand(commandString, connection);
command.Parameters.AddWithValue("id", id);
command.Parameters.AddWithValue("name", name);
command.ExecuteNonQuery();
Now name can have any number of quotes and it'll work fine. More importantly it is now safe from sql injection.
Tools like "dapper" (freely available on NuGet) make this easier:
int id = 1;
string name = "Fred";
connection.Execute("insert into MyTable values (#id, #name)",
new { id, name });
You should look into using parameterized queries. This will allow you insert the data no matter the content and also help you avoid possible future SQL injection.
http://csharp-station.com/Tutorial/AdoDotNet/Lesson06
http://www.c-sharpcorner.com/uploadfile/puranindia/parameterized-query-and-sql-injection-attacks/

How can I prevent inserting duplicate data into a SQL Server table?

I have a series of data that need to be written into SQL, what should I do to check the data in SQL to prevent same data inserted into table?
Example data to be inserted:
David
James
John
If the 4th data is John again, I want the system to skip the duplicate record (John).
So far I have:
SqlConnection myCnn = new SqlConnection(cnn);
String _state = "Insert into CamNo1(platename, date, camID, path, filename) OUTPUT INSERTED.platename values(#msg, getdate(), #camID, #path, #filename)";
SqlCommand _Query = new SqlCommand(_state, myCnn);
_Query.Parameters.AddWithValue("#msg", msg);
_Query.Parameters.AddWithValue("#camID", camID);
_Query.Parameters.AddWithValue("#path", imageFile);
_Query.Parameters.AddWithValue("#filename", name);
try
{
myCnn.Open();
string checkname = (string)_Query.ExecuteScalar();
myCnn.Close();
getcheckname = checkname;
Console.WriteLine("OK");
}
catch (Exception)
{
}
i got the string value checkname that is last inserted, what should i do check the data?
First, you can prevent a duplicate from ever occurring in the table by using a unique index or constraint. An index/constraint can work in concert with the suggestions below. If you only use a unique index and not one of the below solutions, inserting a duplicate record will throw an error and you will need to handle that on the other end.
Additionally, I would probably insert the data via a stored procedure that checks to see if the row already exists. To do that, you can use either a MERGE statement, as shown in this pseudo code:
create procedure MyProcedure
(
#Name nvarchar(100),
...
)
as
merge MyTable
using
(
select #Name,...
) as source (Name, ...)
on MyTable.Name = source.Name
when not matched then
insert (Name,...) values (source.Name,...)
when matched then
update set Name = #Name,...
or, you could check for the records existence and insert or update manually:
create procedure MyProcedure
(
#Name nvarchar(100),
...
)
as
if not exists (select * from MyTable where Name = #Name)
begin
insert into MyTable (Name,...) values (#Name,...)
end
else
begin
update MyTable
set ...
where Name = #Name
end
If you do not want duplicate data, you should consider enforcing that at the DB level with a UNIQUE CONSTRAINT or a UNIQUE INDEX
SQL Server 2008 also has a MERGE statement you could use to check for matched records. This could be helpful if you want to update an existing record.
If you want to prevent duplicate data from being inserted, you could use a unique index or unique constraint on those fields.
If you want to just run a hard insert statement, but have it do nothing if a value exists, something like this should work. I tested this on a local database I have:
declare #subject as varchar(100);
set #subject = 'hello'
insert into Subjects ([name])
select #subject
where not exists (select 1 from Subjects where [name] = #Subject)
Try This Easy way
{
DataSet ds = New DataSet();
SqlConnection myCnn = New SqlConnection(cnn);
myCnn.Open();
SqlCommand _Query = New SqlCommand("Select *FROM CamNo1 where platename='" + Console.ReadLine + "' ", myCnn);
SqlDataAdapter sda = New SqlDataAdapter(_Query);
sda.Fill(ds);
Int i = ds.Tables[0].Rows.Count;
If (i > 0) Then
{
MessageBox.Show("platename" + Console.WriteLine + "Already Exists ");
ds.Clear();
}
Else
{
SqlConnection myCnn = New SqlConnection(cnn);
String _state = "Insert into CamNo1(platename, date, camID, path, filename) OUTPUT INSERTED.platename values(#msg, getdate(), #camID, #path, #filename)";
SqlCommand _Query = New SqlCommand(_state, myCnn);
_Query.Parameters.AddWithValue("#msg", msg);
_Query.Parameters.AddWithValue("#camID", camID);
_Query.Parameters.AddWithValue("#path", i`enter code here`mageFile`);
_Query.Parameters.AddWithValue("#filename", Name);
Try
{
myCnn.Open();
String checkname = (String)_Query.ExecuteScalar();
myCnn.Close();
getcheckname = checkname;
Console.WriteLine("OK");
}
Catch (Exception)
{
}
}
}

Sql Server temporary table disappears

I'm creating a temporary table, #ua_temp, which is a subset of regular table. I don't get an error, but when I try to SELECT from #ua_temp in the second step, it's not found. If I remove the #, a table named ua_temp is created.
I've used the exact same technique from created the table with SELECT INTO elsewhere. It runs fine, so I don't think it has anything to do with database settings. Can anyone see the problem?
// Create temporary table
q = new StringBuilder(200);
q.Append("select policy_no, name, amt_due, due_date, hic, grp, eff_dt, lis_prem, lis_grp, lis_co_pay_lvl, ");
q.Append("lep_prem, lapsed, dn_code, [filename], created_dt, created_by ");
q.Append("into #ua_temp from elig_ua_response ");
q.Append("where [filename] = #fn1 or [filename] = #fn2 ");
sc = new SqlCommand(q.ToString(), db);
sc.Parameters.Add(new SqlParameter("#fn1", sFn));
sc.Parameters.Add(new SqlParameter("#fn2", sFn2));
int r = sc.ExecuteNonQuery();
MessageBox.Show(r.ToString() + " rows");
// Rosters
q = new StringBuilder(200);
q.Append("select policy_no,name,amt_due,due_date,hic,grp,eff_dt,");
q.Append("lis_prem,lis_grp,lis_co_pay_lvl,lep_prem,lapsed,dn_code,[filename] ");
q.Append("from #ua_temp where (lis_prem > 0.00 or lep_prem > 0.00) ");
q.Append("and [filename] = #fn order by name");
sc.CommandText = q.ToString();
sc.Parameters.Clear();
sc.Parameters.Add(new SqlParameter("#fn", sFn));
sda = new SqlDataAdapter(sc);
sda.Fill(ds, "LIS LEP Roster");
To answer some of the obvious questions: This program was running fine using the source table, elig_ua_response. The reason for introducing the temp table was that I want to delete some of the rows for this particular report. I put brackets around the column [filename] while testing to be sure it's not a key word issue. The second SELECT works fine if you replace #ua_temp with elig_ua_response. I've tried different names for the temp table. The MessageBox showing the number of rows was just for debugging purposes; it doesn't affect the problem.
Joe Zack's comment is what helped me understand what's happening here. A very clear and concise explanation. This should be an answer so that it's more visible to people arriving here from a google search.
SqlCommand calls sql with parameters via sp_executesql when there are parameters, which means your temp table gets created inside (and then clean up in) a stored procedure so it's not available to future calls - even when they share the same connection
I think the solution to your problem is to combine the creation of the temp table and selecting from that temp table into one query (see code snippet #3 below). Executing the command twice (as you do in the code in your question) seems to work ok if you are not using command parameters, but fails if they are introduced. I tested a few different approaches and here's what I found.
1) WORKS OK: Use same command object, no command parameters, execute command twice:
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
const string query = #"
CREATE TABLE #temp
([ID] INT NOT NULL, [Name] VARCHAR(20) NOT NULL)
INSERT INTO #temp VALUES(1, 'User 1')
INSERT INTO #temp VALUES(2, 'User 2')";
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * FROM #temp";
using (var sda = new SqlDataAdapter(cmd))
{
var ds = new DataSet();
sda.Fill(ds);
foreach (DataRow row in ds.Tables[0].Rows)
Console.WriteLine("{0} - {1}", row["ID"], row["Name"]);
}
}
}
2) FAILS: Use same command object, command parameters, execute command twice:
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
const string query = #"
CREATE TABLE #temp
([ID] INT NOT NULL, [Name] VARCHAR(20) NOT NULL)
INSERT INTO #temp VALUES(1, #username1)
INSERT INTO #temp VALUES(2, #username2)
";
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
cmd.Parameters.Add("#username1", SqlDbType.VarChar).Value ="First User";
cmd.Parameters.Add("#username2", SqlDbType.VarChar).Value ="Second User";
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
cmd.CommandText = "SELECT * FROM #temp";
using(var sda = new SqlDataAdapter(cmd))
{
var ds = new DataSet();
sda.Fill(ds);
foreach(DataRow row in ds.Tables[0].Rows)
Console.WriteLine("{0} - {1}", row["ID"], row["Name"]);
}
}
}
3) WORKS OK: Use same command object, command parameters, execute command once only:
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
const string query = #"
CREATE TABLE #temp
([ID] INT NOT NULL, [Name] VARCHAR(20) NOT NULL)
INSERT INTO #temp VALUES(1, #username1)
INSERT INTO #temp VALUES(2, #username2)
SELECT * FROM #temp
";
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
cmd.Parameters.Add("#username1", SqlDbType.VarChar).Value ="First User";
cmd.Parameters.Add("#username2", SqlDbType.VarChar).Value ="Second User";
using (var sda = new SqlDataAdapter(cmd))
{
var ds = new DataSet();
sda.Fill(ds);
foreach (DataRow row in ds.Tables[0].Rows)
Console.WriteLine("{0} - {1}", row["ID"], row["Name"]);
}
}
}
This works. Apparently, if the SqlParameters are in the step that creates the table, the table is not left behind for the next step. Once the table is created, the SqlParameters can be used in a separate step for the INSERT.
// Create temporary file dropping members from termed groups.
q = new StringBuilder(500);
q.Append("create table #ua_param ");
q.Append("([ID] int not null, fn varchar(50) not null) ");
sc = new SqlCommand(q.ToString(), db);
sc.ExecuteNonQuery();
q = new StringBuilder(500);
q.Append("insert into #ua_param values(1,#fn1) ");
q.Append("insert into #ua_param values(2,#fn2) ");
sc = new SqlCommand(q.ToString(), db);
sc.Parameters.Add(new SqlParameter("#fn1", sFn));
sc.Parameters.Add(new SqlParameter("#fn2", sFn2));
sc.ExecuteNonQuery();
q = new StringBuilder(500);
q.Append("select policy_no, name, amt_due, due_date, hic, grp, eff_dt, lis_prem, lis_grp, lis_co_pay_lvl, ");
q.Append("lep_prem, lapsed, dn_code, [filename], created_dt, created_by ");
q.Append("into #ua_temp from elig_ua_response inner join #ua_param on [filename] = fn ");
sc.Parameters.Clear();
sc.CommandText = q.ToString();
sc.CommandTimeout = 1800;
sc.ExecuteNonQuery();
Its because the temp table is just that. Temporary. You might consider doing your operations in a stored procedure.
Beyond rolling it into a stored procedure as suggested by #Daniel A White, you can look at BOL article and search for global temporary tables. Also a brief write up on Temporary Tables. Either approach should keep the temporary table alive.
I had the same problem. I tried the SeaDrive solution and it works, however my tests make me believe that the query execution "flushes" something between "ADO.NET/SQLDriver" and the MS SQL Server.
So, you need to isolate the "CREATE TABLE" statement and submit it to the database before to use it with "INSERT INTO". Composed commands joining CREATE and INSERT in one unique statement doesn't work, unless you can give up the parameters.
#TEMP tables only are accessible within the same session or SPID. So if you want to reuse it you need to reuse the connection you used to generate it.
Working example with Dapper:
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
var expected = Guid.NewGuid();
// creating the temp table with NO PARAMETERS PASSED IN is the key part.
conn.Execute("CREATE TABLE #MyTemp (ID UNIQUEIDENTIFIER NOT NULL PRIMARY KEY);");
// now that the temp table is created, you can run queries with params as
// much as you want.
conn.Execute("INSERT INTO #MyTemp (ID) VALUES (#ID)", new { ID = expected });
var actual = conn.Query<Guid>("SELECT ID FROM #MyTemp;").Single();
Assert.Equal(expected, actual); // proof it worked
}
Using a stored proc makes sense for this sort of thing.
If for some reason that's not feasible, then make sure you are using the same connection for the temp table creation as you are for the temp table selection, else the temp table won't be visible. (it might be that you have this issue randomly if you're using connection pooling.) Alternately, use a real, physical table or even a global temp table (##global_tmp vs #local_tmp), but in either case you'll need to devise a scheme/protocol such that multiple processes aren't trying to create/delete/write to that table.
Again, I'll stress that a stored proc would be a good route, if possible.

Getting the Last Insert ID with SQLite.NET in C#

I have a simple problem with a not so simple solution... I am currently inserting some data into a database like this:
kompenzacijeDataSet.KompenzacijeRow kompenzacija = kompenzacijeDataSet.Kompenzacije.NewKompenzacijeRow();
kompenzacija.Datum = DateTime.Now;
kompenzacija.PodjetjeID = stranka.id;
kompenzacija.Znesek = Decimal.Parse(tbZnesek.Text);
kompenzacijeDataSet.Kompenzacije.Rows.Add(kompenzacija);
kompenzacijeDataSetTableAdapters.KompenzacijeTableAdapter kompTA = new kompenzacijeDataSetTableAdapters.KompenzacijeTableAdapter();
kompTA.Update(this.kompenzacijeDataSet.Kompenzacije);
this.currentKompenzacijaID = LastInsertID(kompTA.Connection);
The last line is important. Why do I supply a connection? Well there is a SQLite function called last_insert_rowid() that you can call and get the last insert ID. Problem is it is bound to a connection and .NET seems to be reopening and closing connections for every dataset operation. I thought getting the connection from a table adapter would change things. But it doesn't.
Would anyone know how to solve this? Maybe where to get a constant connection from? Or maybe something more elegant?
Thank you.
EDIT:
This is also a problem with transactions, I would need the same connection if I would want to use transactions, so that is also a problem...
Using C# (.net 4.0) with SQLite, the SQLiteConnection class has a property LastInsertRowId that equals the Primary Integer Key of the most recently inserted (or updated) element.
The rowID is returned if the table doesn't have a primary integer key (in this case the rowID is column is automatically created).
See https://www.sqlite.org/c3ref/last_insert_rowid.html for more.
As for wrapping multiple commands in a single transaction, any commands entered after the transaction begins and before it is committed are part of one transaction.
long rowID;
using (SQLiteConnection con = new SQLiteConnection([datasource])
{
SQLiteTransaction transaction = null;
transaction = con.BeginTransaction();
... [execute insert statement]
rowID = con.LastInsertRowId;
transaction.Commit()
}
select last_insert_rowid();
And you will need to execute it as a scalar query.
string sql = #"select last_insert_rowid()";
long lastId = (long)command.ExecuteScalar(sql); // Need to type-cast since `ExecuteScalar` returns an object.
last_insert_rowid() is part of the solution. It returns a row number, not the actual ID.
cmd = CNN.CreateCommand();
cmd.CommandText = "SELECT last_insert_rowid()";
object i = cmd.ExecuteScalar();
cmd.CommandText = "SELECT " + ID_Name + " FROM " + TableName + " WHERE rowid=" + i.ToString();
i = cmd.ExecuteScalar();
I'm using Microsoft.Data.Sqlite package and I do not see a LastInsertRowId property. But you don't have to create a second trip to database to get the last id. Instead, combine both sql statements into a single string.
string sql = #"
insert into MyTable values (null, #name);
select last_insert_rowid();";
using (var cmd = conn.CreateCommand()) {
cmd.CommandText = sql;
cmd.Parameters.Add("#name", SqliteType.Text).Value = "John";
int lastId = Convert.ToInt32(cmd.ExecuteScalar());
}
There seems to be answers to both Microsoft's reference and SQLite's reference and that is the reason some people are getting LastInsertRowId property to work and others aren't.
Personally I don't use an PK as it's just an alias for the rowid column. Using the rowid is around twice as fast as one that you create. If I have a TEXT column for a PK I still use rowid and just make the text column unique. (for SQLite 3 only. You need your own for v1 & v2 as vacuum will alter rowid numbers)
That said, the way to get the information from a record in the last insert is the code below. Since the function does a left join to itself I LIMIT it to 1 just for speed, even if you don't there will only be 1 record from the main SELECT statement.
SELECT my_primary_key_column FROM my_table
WHERE rowid in (SELECT last_insert_rowid() LIMIT 1);
The SQLiteConnection object has a property for that, so there is not need for additional query.
After INSERT you just my use LastInsertRowId property of your SQLiteConnection object that was used for INSERT command.
Type of LastInsertRowId property is Int64.
Off course, as you already now, for auto increment to work the primary key on table must be set to be AUTOINCREMENT field, which is another topic.
database = new SQLiteConnection(databasePath);
public int GetLastInsertId()
{
return (int)SQLite3.LastInsertRowid(database.Handle);
}
# How about just running 2x SQL statements together using Execute Scalar?
# Person is a object that has an Id and Name property
var connString = LoadConnectionString(); // get connection string
using (var conn = new SQLiteConnection(connString)) // connect to sqlite
{
// insert new record and get Id of inserted record
var sql = #"INSERT INTO People (Name) VALUES (#Name);
SELECT Id FROM People
ORDER BY Id DESC";
var lastId = conn.ExecuteScalar(sql, person);
}
In EF Core 5 you can get ID in the object itself without using any "last inserted".
For example:
var r = new SomeData() { Name = "New Row", ...};
dbContext.Add(r);
dbContext.SaveChanges();
Console.WriteLine(r.ID);
you would get new ID without thinking of using correct connection or thread-safety etc.
If you're using the Microsoft.Data.Sqlite package, it doesn't include a LastInsertRowId property in the SqliteConnection class, but you can still call the last_insert_rowid function by using the underlying SQLitePCL library. Here's an extension method:
using Microsoft.Data.Sqlite;
using SQLitePCL;
public static long GetLastInsertRowId(this SqliteConnection connection)
{
var handle = connection.Handle ?? throw new NullReferenceException("The connection is not open.");
return raw.sqlite3_last_insert_rowid(handle);
}

Categories

Resources