How to get Indentity value back after insert - c#

Given the following code (which is mostly irrelevant except for the last two lines), what would your method be to get the value of the identity field for the new record that was just created? Would you make a second call to the database to retrieve it based on the primary key of the object (which could be problematic if there's not one), or based on the last inserted record (which could be problematic with multithreaded apps) or is there maybe a more clever way to get the new value back at the same time you are making the insert?
Seems like there should be a way to get an Identity back based on the insert operation that was just made rather than having to query for it based on other means.
public void Insert(O obj)
{
var sqlCmd = new SqlCommand() { Connection = con.Conn };
var sqlParams = new SqlParameters(sqlCmd.Parameters, obj);
var props = obj.Properties.Where(o => !o.IsIdentity);
InsertQuery qry = new InsertQuery(this.TableAlias);
qry.FieldValuePairs = props.Select(o => new SqlValuePair(o.Alias, sqlParams.Add(o))).ToList();
sqlCmd.CommandText = qry.ToString();
sqlCmd.ExecuteNonQuery();
}
EDIT: While this question isn't a duplicate in the strictest manner, it's almost identical to this one which has some really good answers: Best way to get identity of inserted row?

It strongly depends on your database server. For example for Microsoft SQL Server you can get the value of the ##IDENTITY variable, that contains the last identity value assigned.
To prevent race conditions you must keep the insert query and the variable read inside a transaction.
Another solution could be to create a stored procedure for every type of insert you have to do and make it return the identity value and accept the insert arguments.
Otherwise, inside a transaction you can implement whatever ID assignment logic you want and be preserved from concurrency problems.

Afaik there is not finished way.
I solved by using client generated ids (guid) so that my method generated the id and returns it to the caller.
Perhaps you can analyse some SqlServer systables in order to see what has last changed. But you would get concurrency issues (What if someone else inserts a very similar record).
So I would recommend a strategy change and generate the id's on the clients

You can take a look at : this link.
I may add that to avoid the fact that multiple rows can exist, you can use "Transactions", make the Insert and the select methods in the same transaction.
Good luck.

The proper approach is to learn sql.
You can do a SQL command followed by a SELECT in one run, so you can go in and return the assigned identity.
See

Related

Populate new field in SQLite database using existing field value and a C# function

I have a SQLite database for which I want to populate a new field based on an existing one. I want to derive the new field value using a C# function.
In pseudocode, it would be something like:
foreach ( record in the SQLite database)
{
my_new_field[record_num] = my_C#_function(existing_field_value[record_num]);
}
Having looked at other suggestions on StackOverflow, I'm using a SqliteDataReader to read each record, and then running a SQlite "UPDATE" command based on the specific RowId to update the new field value for the same record.
It works .... but it's REALLY slow and thrashes the hard drive like crazy. Is there really no better way to do this?
Some of the databases I need to update might be millions of records.
Thanks in advance for any help.
Edit:
In response to the comment, here's some real code in a legacy language called Concordance CPL. The important point to note is that you can read and write changes to the current record in one go:
int db;
cycle(db)
{
db->FIRSTFIELD = myfunction(db->SECONDFIELD);
}
myfunction(text input)
{
text output;
/// code in here to derive output from input
return output;
}
I have a feeling there's no equivalent way to do this in SQLite as SQL is inherently transactional, whereas Concordance allowed you to traverse and update the database sequentially.
The answer to this is to wrap all of the updates into a single transaction.
There is an example here that does it for bulk inserts:
https://www.jokecamp.com/blog/make-your-sqlite-bulk-inserts-very-fast-in-c/
In my case, it would be bulk updates based on RowID wrapped into a single transaction.
It's now working, and performance is many orders of magnitude better.
EDIT: per the helpful comment above, defining a custom C# function and then reference it in a single UPDATE command also works well, and in some ways is better than the above as you don't have to loop through within C# itself. See e.g. Create/Use User-defined functions in System.Data.SQLite?

SQL Server prevent only specific table from updating based on parameter passed from C# controller

In our .net application, we have a tool that allows you to type SQL in a browser and submit it, for testing. In this context, though, I need to be able to prevent testers from writing to specific tables. So, based on the parameter passed from the controller (InSupportTool = true, or something), I need to know if SQL Server is allowed to make updates or inserts to, say, an accounts table.
Things I've tried so far:
I have tried looking into triggers, but there is no before trigger available, and I've heard people don't recommend using them if you can help it.
Parsing the passed SQL string to look for references to inserting or updating on that table. This is even more fragile and has countless ways, I'm sure, of getting around it if someone wanted to.
Check constraint, which is the closest I feel I've gotten but I can't quite put it together.
For check constraints, I have this:
ALTER TABLE Accounts WITH NOCHECK
ADD CONSTRAINT chk_read_only_accounts CHECK(*somehow this needs to be dynamic based on parameters passed from C# controller*)
The above works to prevent updates to that table, but only if I put a check like 1 = 0. I've seen a post where people said you could use a function as the check, and pass parameters that way, but I'm at the limit of my familiarity with SQL/.net.
Given what I'm looking to do, does anyone have experience with something like this? Thanks!
Since the application is running under a different account than the end user, you could specify your application name in the connection string (e.g. Application Name=SupportTool) and check that in an after trigger, rolling back the transaction as needed:
CREATE TABLE dbo.example(
col1 int
);
GO
CREATE TRIGGER tr_example
ON dbo.example
AFTER INSERT, UPDATE, DELETE
AS
IF APP_NAME() = N'SupportTool'
BEGIN
ROLLBACK;
THROW 50000, 'This update is not allowed using the support tool', 1;
END;
GO
INSERT INTO dbo.example VALUES(1);
GO

How to update List<string> column using Mapper.UpdateAsync in Cassandra?

I am a newbie to Cassandra and my current project called for me to create a table with the following columns:
id uuid PRIMARY KEY,
connections list<text>,
username text
I am using Cassandra's IMapper interface to handle my CRUD operations. While I found documentation that describes how to use the Mapping component for basic operations here:
http://docs.datastax.com/en/developer/csharp-driver/2.5/csharp-driver/reference/mapperComponent.html
I could not find documentation that outlines how to add and remove items from the List column for a specific record using the Mapper component. I tried to retrieve the record from the database, update the entity and save the changes to the record but the record is not updated in the database. It remains the same after the Update. However, the insert operation works and it mirrors the entity down to the object in the list.
User user = await _mapper.SingleAsync<T>("where Name = " + name);
user.Addresses = user.Addresses.Concat(new string[] { address });
await _mapper.UpdateAsync<T>(user);
How should this scenario be handled in Cassandra?
You can use the plus (+) and minus (-) CQL operators to append / prepend or remove items from a list.
In your case it would be:
// When using parameters, use query markers (?) instead of
// hardcoded stringified values
User user = await _mapper.SingleAsync<User>("where id = ?", id);
await _mapper.UpdateAsync<T>(
"SET connections = connections + ? WHERE id = ?", newConnections, id");
Note that append and prepend operations are not idempotent by nature. So in particular, if one of these operation timeout, then retrying the operation is not safe and it may (or may not) lead to appending/prepending the value twice.
I think in order for this to work and be efficient you may need several things:
partial update
It is atomic and doesn't require you to fetch the record first. Also, specifying only the fields you want to update avoids passing unnecessary load on the wire and relieves the pressure on the compactor.
use CqlOperator.Append/CqlOperator.Prepend/CqlOperator.SubstractAssign
Which allow you to specify only the collection items you want to add/remove.
Both of these optimizations are available via the Table API, not sure about the Mapper.

dapper: #tempDB created but not available in next query

In my program, I want to select some bookIDs into a tempDB for later queries like this (using Dapper extension):
using (var conn = new SqlConnection(connStr)) {
conn.Execute("SELECT bookID INTO #tempdb WHERE ... FROM Books");
int count = conn.ExecuteScalar<int>("SELECT COUNT(*) FROM #tempdb");
var authors = conn.Query("SELECT * FROM #tempdb LEFT JOIN BookAuthors ON ...");
}
However when I execute the page, I get following exception:
Invalid object name '#tempdb'.
It seems that life-cycle of #tempdb is only valid in first query ?
It looks like you're using the implicit connection opening/closing. This will indeed cause problems with transient objects. If you need temp tables between queries, you will need to manually open the connection before you execute any such queries. This should then work fine, and many examples in the test suite make use of temp tables in the way.
However, from a practical standpoint, making use of temporary tables to transfer state between queries is ... awkward. In addition to being brittle, it isn't good for the plan cache, as #foo has a different meaning between all uses on different connection (including reset but reused connections).
I found a previous poster who met the same problem and his solution.
Using dapper, why is a temp table created in one use of a connection not available in a second use of the same connection
The post indicates that you have to "CREATE TABLE #tempdb" explicitly in your SQL first and everything goes fine. Even the poster himself don't know why such style of coding works.

How to restrict sqlite only to execute SELECT statements, and not INSERT, UPDATE

I have a reports page where you can enter the query manually for a report. How can I block any INSERT, UPDATE or DELETE statements, and only run SELECT?
using (var connection = new SQLiteConnection(connectionString))
{
var da = new SQLiteDataAdapter
{
SelectCommand = new SQLiteCommand(query, connection)
};
try
{
da.Fill(table);
}
I could check if the query string contains "INSERT", "UPDATE" or "DELETE", but I don't think it's a good practice.
You could use an EXPLAIN statement to break the query down into VM instructions and examine the opcode column of the output. If the value "OpenWrite" occurs then the query is not read-only.
Checking the query string is not good practice? Compared to what? Allowing a user to enter any SQL statement they want to in your report page? I can't think of a much worse practice than that. If you're going to allow that sort of thing, you absolutely need to somehow restrict the types of statements they enter, and maybe require a Where clause (to avoid millions of rows being returned) etc.
in fact did you check what happens when you try to fill the table with the data adapter having anything else than a select in the query variable? I doubt you get an empty table or dataset, I would expect an exception in which case you could rollback the transaction.
I would anyway try to create the connection as readonly as suggested above by Sorax and I would actually parse the query variable as well.
Since the SQlite database is just one file, my guess is that you can make the database readonly through the filesystem. This is of course not a fancy solution but is one that does not require any code (of course except when you're throwing exceptions when writing isn't possible).
A) Use a read-only connection - I think that would be almost the best solution
B) Use more than one TextBox as Input (but this would become more a solution like checking the String for Insert etc.)
For Example
Select |____| From |_________| Where |_______|
Edit: to answer your comment just have a look at http://www.sqlite.org/c3ref/open.html especially the topic "SQLITE_OPEN_READONLY" - I haven't done anything with sqlite now, but I think that should do the trick...

Categories

Resources