How to use NpgsqlParameter with ExecuteSqlCommand

2019-09-08 23:10发布

问题:

I am using Entity Framework with PostgreSQL, which means I'm using Npgsql as the data provider. I would like to call context.Database.ExecuteSqlCommandAsync to make a query, and I'm trying to use parameters to avoid injection attacks. One example of a query I would like to make is alter table add column. Here is what my code looks like to set up the parameters and the sql query:

NpgsqlParameter tableNameParam = new NpgsqlParameter("tableName", NpgsqlDbType.Text);
tableNameParam.Value = entityTableName;
NpgsqlParameter fieldNameParam = new NpgsqlParameter("fieldName", NpgsqlDbType.Text);
fieldNameParam.Value = sqlFieldName;
NpgsqlParameter fieldTypeParam = new NpgsqlParameter("fieldType", NpgsqlDbType.Text);
fieldTypeParam.Value = sqlType;

object[] sqlParams = {tableNameParam, fieldNameParam, fieldTypeParam};

string addColumnQuery = "ALTER TABLE @tableName ADD @fieldName @fieldType";
await _context.Database.ExecuteSqlCommandAsync(addColumnQuery, sqlParams);

When I run this I catch an NpgsqlException with

error 42601: syntax error at or near \"(\"".

I looked into the stack trace and found that the ErrorSQL displays:

"ALTER TABLE (('dbo.person_entity')::text) ADD (('name')::text)(('varchar')::text)"

As you can see the query went horribly wrong. In my test I am using a person entity and adding a column "name" with type "varchar". I just want the query to be like this:

"ALTER TABLE dbo.person_entity ADD name varchar"

But for some reason using NpgsqlParameters is messing that up. I suspect it has to do with the NpgsqlDbType.Text but it seems like the NpgsqlParameter constructor requires a type as the second argument.

回答1:

First, PostgreSQL doesn't support parameters for column/table names. There are some good reasons for this behavior: it doesn't normally make sense to allow users to create arbitrary fields with arbitrary types in your databases - even if you sanitize them to prevent SQL injection. Try to think of redesigning your application.

For a more technical explanation of what's happening in your case, the Npgsql Entity Framework provider replaces your parameter placeholder (e.g. @fieldName) with a text literal - and (('dbo.person_entity')::text) is a perfectly valid literal. The problem is that column names and types aren't text literals: you don't surround them with quotes.