Passing an array to SQL parameter with Dapper - c#

I want to insert into a table an array of values if they don't exist, the array is small and would not exceed 10 items, so it is safe to pass in an insert.
How can I execute this code with Dapper? I tried the following but I am getting an error:
const string sqlSymbolsInsert =
#"INSERT INTO Country (Name)
SELECT NewNames.Name FROM (VALUES(#Names)) AS NewNames (Name)
WHERE NOT EXISTS (SELECT 1 FROM Country AS C WHERE C.Name = NewNames.Name);";
await using var cn = new SqlConnection(CONNECTION_STRING);
await cn.ExecuteAsync(sqlSymbolsInsert, new { Names = countries.Select(x => x.Name) });
The error is:
Core Microsoft SqlClient Data Provider: Incorrect syntax near ','.
There is a similar problem on SO, but it is for the IN clause:
dapper "IN" clause not working with multiple values
Is there another way in Dapper to pass an array in my case?

What you are trying to do isn't possible. But instead of the array, you can just use your countries collection. It has a Name property and Dapper will run the query for each item in a suitable collection:
const string sqlSymbolsInsert =
#"INSERT INTO Country (Name)
SELECT #Name WHERE NOT EXISTS
(
SELECT 1
FROM Country
WHERE Name = #Name
)";
await using var cn = new SqlConnection(CONNECTION_STRING);
await cn.ExecuteAsync(sqlSymbolsInsert, countries);

Related

DB2 dynamic sql, variables, returning scalar value

I have a table with:
ID (int, generated)
OriginalID (int, nullable)
...
other columns.
When inserting a record I would like to set the "OriginalID" to the "ID" that was generated. Currently the script looks like this:
BEGIN ATOMIC
DECLARE i INT;
SET i = SELECT ID FROM NEW TABLE(INSERT INTO MyTable(...) VALUES (....));
UPDATE MyTable SET ORIGINALID=i WHERE ID=i;
END
now the problem I have is how to return the "i", to get the value in code, when calling ExecuteScalar method?
I have already tried "SELECT i FROM SYSIBM.SYSDUMMY1", "VALUES (i)" - both statements return a "null" in code.
I do get the value if I call ExecuteScalar with script like this:
SELECT ID FROM NEW TABLE(INSERT INTO MyTable(...) VALUES (....));
The query is executed from c# code (.net core/IBM.Data.DB2.Core 1.2.2.100) by using a DB2Connection and DB2Command:
var query = ...;
var connection = new DB2Connection(conn_string);
var command = new DB2Command(connection, query);
command.parameters.Add(...);
var returnValue = command.ExecuteScalar(); << the value here is always
null if trying to return the db2 variable; it works only if executing
"single statement"
**** edit ****
Got it working with help of Mark Barinstein:
last statement in query is now: "set #newId = i;" and in code:
var newId = new DB2Parameter("#newId", DB2Type.Integer);
newId.Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(newId);
command.ExecuteNonQuery();
var id = newId.Value;

Dapper can't recognize wildcard param in the single quotes

I am trying to get indexes fragmentation info from database.
Here the dapper sql query:
var result = await _dbConnection.QueryAsync<IndexFragmentationModel>($#"
select
a.index_id as Id, name as Name, avg_fragmentation_in_percent as FragmentationPercent
from sys.dm_db_index_physical_stats (DB_ID(N'#dbName'), OBJECT_ID(N'#tableName'), null, null, null) as a
join sys.indexes as b on a.object_id = b.object_id and a.index_id = b.index_id;
", new
{
dbName = dbName,
tableName = tableName
});
return result.ToList();
Parameters are not passing the the places where they are expected.
Could anybody please suggest - maybe there is another way to pass them ?
You're using the literal strings "#dbName" and "#tableName", not the parameters' values.
Remove the N' and ' that surround them.

How to get specific columns returned by stored procedure in variable/list using Entity Framework Core

I want to get list of specific columns returned by stored procedure.In the result set two column names will be common and one column name will be dynamic.
ALTER PROC DBO.GETLANGUAGETRANSLATION(#LANGCODE VARCHAR(10))
AS
BEGIN
DECLARE #QUERY NVARCHAR(255)
SET #QUERY= N'SELECT RESOURCENAME,ENText,'+UPPER(#LANGCODE)+' INTO #TEMP FROM DBO.LANGUAGETRANSLATION SELECT * FROM #TEMP';
EXEC(#QUERY);
END
RESOURCENAME and ENText column will be same in output each time but based in input parameter third column name will vary.
I am writing below code in C# using Entity Framework Core LINQ
public async Task<IEnumerable<LanguageTranslation>> GetAsync(string langCode)
{
var context = new LQMSDbContext(AppConstants.DB_CONNECTION_STRING_KEY);
try
{
string query = "GetLanguageTranslation '" + langCode + "'";
var result = context.LanguageTranslation.FromSql(query).ToList();
var result1 = context.Database.ExecuteSqlCommand("GETLANGUAGETRANSLATION #p0", parameters: langCode );
}
catch(Exception ex)
{
string a = ex.Message;
}
return await _dbSet
.Where(x => x.Equals(langCode))
.ToListAsync();
}
I am trying to call stored procedure GETLANGUAGETRANSLATION using two different approaches. But both are failing with below error
The required column 'ESText' was not present in the results of a
'FromSql' operation
Where ESText refers to the column which I am not returning from stored procedure but present in table.
I want to store only few columns in result set in c# and not all.
Can any body help me in this?
NOTE : It works fine with Select * from LanguageTranslation query
Have a look at how to execute raw queries.
https://learn.microsoft.com/en-us/ef/core/querying/raw-sql
You can write the query to store the results temporarily in a temp table then select from that table.
FromSql takes more parameters for your injection, you will want to use those extra parameters to place yours into the query. Don't string concatenate.
Another note: when you use ToList or ToArray or Count you will execute the query on the server.

Cosmosdb : "message":"Syntax error, invalid numeric value token '4d5f'." error

I am trying to query some information from the document db, it used to work earlier when we have id field , I recently replaced with Guid field and when we run the following query it throws the error as follows
Code:
queryString = string.Format(
"SELECT f.keys.Timestamp,f.keys.selectedFieldId FROM {0} f WHERE f.logevent = '{1}' AND f._ts > {2} AND f._ts <= {3}",
DocumentDbRepository.DatabaseId, Enums.Events.ApplicationAuthenticationSuccess, refUnixTime,
currentUnixTime);
ERROR:
"message":"Syntax error, invalid numeric value token '4d5f'."
any help would be appreciated!
If this is the replacement value of {0} in the FROM clause, then it has to conform to a valid identifier (i.e. starts with an alphabetic character or underscore).
Anyways, you really don't need to specify the database Id here, any identifier will do since the collection is always the one you're connected to. In other words, you could write the query as 'SELECT ... FROM f WHERE ...)
I also faced similar issue while using Cosmos DB Java API. It got resolved using SqlQuerySpec APIs
Use DocumentClient.queryDocuments(String collectionLink, SqlQuerySpec querySpec,FeedOptions options)
#param1 and #param2 are placeholder in query string and will be replaced by id and user when we set to SqlParameter. When SqlParameterCollection and query are passed to SQLQuerySpec , it replace placeholder values from query string with corresponding value againt key userd in SqlParameterCollection.
//Create SQLQuerySpec instance as below
String query = "SELECT * FROM mycollections m WHERE w.id =#param1 and w.user
=#param2";
SqlParameterCollection col = new SqlParameterCollection();
SqlParameter sqlParameter1 = new SqlParameter("#param1", id);
SqlParameter sqlParameter2 = new SqlParameter("#param1", user);
col.add(sqlParameter1);
col.add(sqlParameter2);
SQLQuerySpec sqlspec = new SQLQuerySpec(query,col);
FeedOptions options = new FeedOptions()
options.setEnableCrossPartitionKey(true);
List<Documents> results = documentClient.queryDocuments(collectionLink,
sqlSpec,options).getQueryIterable().toList()

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