Npgsql parameterized query output incompatible with PostGIS - c#

I have this parameterized query in an Npgsqlcommand:
UPDATE raw.geocoding
SET the_geom = ST_Transform(ST_GeomFromText('POINT(:longitude :latitude)', 4326),3081)
WHERE id=:id
:longutide and :latitude are double, and id is int.
The query that is actually run against the DB looks like this:
UPDATE raw.geocoding
SET the_geom = ST_Transform(ST_GeomFromText('POINT(((E'-96.6864379495382')::float8) ((E'32.792527154088')::float8))', 4326),3081)
WHERE id=((10793455)::int4)
Thanks to help from Erwin Brandstetter here, it's apparent that the query needs to be simplified to work with PostGIS. He suggested this:
UPDATE raw.geocoding
SET the_geom = ST_Transform(ST_GeomFromText(
$$POINT(:longitude :latitude)$$::geometry, 4326), 3081)
WHERE id = :id
I guess I could create this with a dynamic query, where I manually update the query every time I run it, but is there a way to make this work with a Npgsql parameterized query?

I am not an expert with npgsql, but I think your parameterized query could work like this:
UPDATE raw.geocoding
SET the_geom = ST_Transform(ST_GeomFromText(:mygeom, 4326), 3081)
WHERE id = :id
And mygeom would hold this string:
POINT(96.6864379495382 32.792527154088)
.. pre-assembled from your other variables. Would result in a query like this:
UPDATE raw.geocoding
SET the_geom = ST_Transform(ST_GeomFromText(
(E'POINT(96.6864379495382 32.792527154088)')::text, 4326),3081)
WHERE id=((10793455)::int4)
Which should work.
If you have trouble assembling the string (like your comment demonstrates), there is a more elegant way. As per hint from #Paul on my previous answer - PostGIS provides a dedicated function for the purpose:
ST_MakePoint(double precision x, double precision y)
Details in the manual. With this, we finally arrive at:
UPDATE raw.geocoding
SET the_geom = ST_Transform(ST_SetSRID(
ST_MakePoint(:longitude, :latitude), 4326), 3081)
WHERE id = :id
Note the comma. Will it finally work now?
If not, just beat it with a sledgehammer. Grml.
It does - with ST_SetSRID() now instead of ST_GeomFromText(). See comment.

In my case, I used:
NpgsqlCommand command = new NpgsqlCommand(
"select ST_Distance( ST_SetSRID(" +
"ST_MakePoint(#longitude, #latitude), 4326)," +
"(select geom from segments where segment_id= #id )," +
"true)",
m_DBConnection);
And it worked. Also, try with:
NpgsqlCommand command = new NpgsqlCommand(
"select ST_AsText( ST_ClosestPoint( ST_GeomFromText('POINT(" +
longitude.ToString(CultureInfo.GetCultureInfo("en-US").NumberFormat) + " " +
latitude.ToString(CultureInfo.GetCultureInfo("en-US").NumberFormat) + ")', 4326)," +
"(select geom from segments where segment_id= #id )))",
m_DBConnection);
Thanks.

Related

Invalid Column Name: "value" - Error Even though it works in another form.

I am stuck at one problem and I just can't solve this.
I get this Error:
Error Message
That's the relevant table
The Code:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = " + #departmentCB.Text;
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#nextMaintainance", nextMaintainanceDT.Value);
command.ExecuteNonQuery();
The weird thing I don't understand is that a similar code works just fine without any error in my project:
query = "UPDATE LDV SET received = #received, department = #department WHERE Id =" + #idTxt.Text;
command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#received", inDT.Value);
command.Parameters.AddWithValue("#department", departmentCb.Text);
command.ExecuteNonQuery();
MessageBox.Show("Lungenautomat wurde aktualisiert");
If relevant, my connection string:
connectionString = ConfigurationManager.ConnectionStrings["SCBA_Manager_0._1.Properties.Settings.SCBAmanagerConnectionString"].ConnectionString;
I really hope you can help me :(
Thank you!
The department column is a text column, so comparing it to a value means the value should be wrapped in quotes.
// This fix is not the recommended approach, see the explanation after this code block
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = '" + departmentCB.Text + "'";
// ^--------------------------^------ single quote added to wrap the value returned by departmentCB.Text
On the other hand, this error does not occur in your second example, because there you're correctly using the Parameters.AddWithValue() method to add the value for the #department parameter, and because id is a numeric column, so it doesn't require the value wrapped in quotes.
However, while the code shown above does the job, it is not the right way of doing the job. The correct way is to used parameters for all values to be injected into a query. The queries you've shown above are already correctly using parameters for some values (e.g. nextMaintenance in the first query, received and department in the second), but are incorrectly doing string concatenation for other values (e.g. department in the first query, id in the second).
Usage of Parameterized SQL
The benefit of using parameterized SQL is that it automatically takes care of adding quotes, prevents SQL injection, etc.
Therefore, its best to change your first code block to:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = #department";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#department", departmentCb.Text);
command.Parameters.AddWithValue("#nextMaintainance", nextMaintainanceDT.Value);
command.ExecuteNonQuery();
Notice how the string query is a single string without any messy concatenation, and that it contains two parameters #nextMaintenance and #department? And how the values for those parameters are correctly injected using Parameters.AddWithValue() in the following lines?
Your second code block can be similarly improved by using a parameter for the Id column.
query = "UPDATE LDV SET received = #received, department = #department WHERE Id = #Id ";
command.Parameters.AddWithValue("#Id", idTxt.Text);
Further Information
Do read up about SQL injection ( https://technet.microsoft.com/en-us/library/ms161953(v=sql.105).aspx ) to see how using string concatenation like your original code can lead to various security issues, and why parameterized queries are the preferred way of injecting dynamic values into SQL queries.
You can read up more about parameterized queries here: https://msdn.microsoft.com/en-us/library/yy6y35y8(v=vs.110).aspx
In your first example, the WHERE clause evaluates to
WHERE department = Kasseedorf
wheras it should be
WHERE department = 'Kasseedorf'
So the line should be
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = '" + #departmentCB.Text +"'";
It works in the second example, because id is an integer and doesn't neet quotes.

C# Using sql parameters which now won't work

Hey I was using parametrized queries for my application which worked just fine but now (I don't know why) they aren't replaced anymore with the values...
So instead of running something like "SELECT [TABLE_NAME] FROM [MyDefinetelyExistingDatabase]"; it tries to execute "SELECT [TABLE_NAME] FROM [#targetDatabase]"; which, of course, will fail.
var dataBaseToGetTablesFrom = "MyDefinetelyExistingDatabase";
var results = new List<string>();
const string query = #"SELECT
[TABLE_NAME] AS tableName
FROM
[#targetDatabase].[INFORMATION_SCHEMA].[TABLES] ;";
using (var context = new ConnectionHandler(true))
{
if (context.Connection.State != ConnectionState.Open)
throw new ConnectionFailedException(context.Connection.State);
using (var command = new SqlCommand(query, context.Connection))
{
command.Parameters.AddWithValue("#targetDatabase", dataBaseToGetTablesFrom);
using (var reader = command.ExecuteReader())
{
if (!reader.HasRows)
return results.ToArray();
while (reader.Read())
results.Add(reader.GetString(0));
}
}
}
return results.ToArray();
I now tried different formats and things to add the parameters but it results in the same...
I don't want to do this by inserting the values into the query directly via string.Format eg but I want to have those parameters (which work properly at different places in the code (???) but not where I want.
In fact, I need to use parameters in every statement and must be able to address different databases by calling them like [DB].[Table-Schema].[Table]
[EDIT]
Hey guys, figured the problem some days ago and thought I share it with you.
As far as I have noticed, my problem at the whole was to try to replace the databasename and / or in some other examples, the table name as well.
So this won't work which makes clearly sense to me as the server can't prepare to execute a statement if it doesn't even know on which table it should work and therefore doesn't know anything about the structure etc.
So I changed my statements to fit my new knowledge and it worked as expected like a charm.
I don't know what ConnectionHandler is, but if that is your own code you can implement it with SqlConnectionStringBuilder which will allow you to use a variable to assign the InitialCatalog instead of putting the database name in the query. This would be preferable to dynamic sql which requires careful sanitization.
You would need dynamic sql for this something like.....
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' SELECT [TABLE_NAME] AS tableName '
+ N' FROM ' + QUOTENAME(#targetDatabase) + N'.[INFORMATION_SCHEMA].[TABLES]'
Exec sp_executesql #Sql

Update multiple fields

First of all i'm working with the MySQL-Connector / Net and a MySQL Database.
And please don't tell me that i should use using or i don't have try and catch. I have but i just wanted to post a low amount of code.
I want to update multiple fields at one but it is not working. I'm getting an syntax error.
(from comments)I'm getting this error:
MySql.Data.MySqlClient.MySqlException (0x80004005): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '+ 1, allC = allC + 50' at line 1
sql_command.CommandText = "Update Test Set x1 + 1 And allC + ?Ammount Where = 1;";
sql_command.Parameters.Add("?Ammount", MySqlDbType.Int32).Value = dataTable.Tables[0].Rows.Count;
sql_command.ExecuteNonQuery();
But isn't this right?
I don't really need the where clause because it is just a number table i would say, so there is not more than one row.
But it is also not working if it try it so:
sql_command.CommandText = "Update Test Set x1 + 1 And all + ?Ammount;";
And there is another question i have.
If i want to get one entry from a database and it's just really one, which is the easiest way to do that?
That's it, but how can i save this record in a string with a low amount of code?
sql_command.CommandText = "Select ID From Customer Order By ID Desc Limit 1;";
Assign your expressions to a column and use a comma (,) instead of And:
Update Test
Set x1 = x1 + 1, all = all + ?Ammount
Where = 1;
Also, your WHERE clause is invalid, but I need to more info to fix it for you.
Your SQL syntax for the UPDATE statement is broken beyond repair:
Update Test Set x1 + 1 And all + ?Ammount Where = 1;
To which field WHERE = 1 should refer to?
Which field should receive value of the x1 + 1?
I don't believe this question can be answered properly unless it is significantly revised; please provide some kind of a description on what you want to do with the UPDATE statement.
Also, last SELECT statement can be replaced by the following:
SELECT MAX(ID) FROM Customer;
This would be a bit more readable.

SQL Syntax in C#

I'm trying to understand why in C# if you have a sql string why you would have to put tick (') marks in the following where clause in order for this to work. Could someone please explain the reasoning behind this?
where ProgramServer='" + machineName.ToString() + "' and Active=1;
You can avoid those tick (') marks and use Parameters, They will also save you from SQL Injection.
The reason you see those ticks are because SQL expects string type values to be enclosed in single ticks.
What you're seeing is a dynamically built SQL query in the code. When querying based on a string value, the string must be wrapped in single quotes. The final SQL string would look something like:
select * from someTable where ProgramServer = 'YourMachineName' and Active = 1;
Unfortunately, that is far from the best way to do things. You should be using parameterized queries instead:
var query = "select * from someTable where ProgramServer = #machineName and Active = 1;";
using(var conn = new SqlConnection(connString))
{
var command = new SqlCommand(query, conn);
command.Parameters.Add("machineName", machineName.ToString());
// Execute and get the results
}

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