I'm trying to execute a query that will get me a value from my postgre database using c# with the npgsql plugin. I got the query from here.
https://dba.stackexchange.com/a/90567
This is the query
string query = String.Format(#"SELECT a.attrelid::regclass::text, a.attname
, CASE a.atttypid
WHEN 'int'::regtype THEN 'serial'
WHEN 'int8'::regtype THEN 'bigserial'
WHEN 'int2'::regtype THEN 'smallserial'
END AS serial_type
FROM pg_attribute a
JOIN pg_constraint c ON c.conrelid = a.attrelid
AND c.conkey[1] = a.attnum
JOIN pg_attrdef ad ON ad.adrelid = a.attrelid
AND ad.adnum = a.attnum
WHERE a.attrelid = '""public.{0}""'::regclass
AND a.attnum > 0
AND NOT a.attisdropped
AND a.atttypid = ANY('{int,int8,int2}'::regtype[])
AND array_length(c.conkey, 1) = 1
AND ad.adsrc = 'nextval('''
|| (pg_get_serial_sequence (a.attrelid::regclass::text, a.attname))::regclass
|| '''::regclass)'; ", record);
It's giving me a
Input string was not in a correct format.
When it goes through that variable. I only have 1 variable and that's the {0}, but I don't know why it's yelling at me with that.
EDIT:
Btw, the double quotes(") are needed because it is created using entity framework and that's just how it works when you execute the query on pgAdminIII. All the tables needs to be inside them.
Related
I'm working on a WPF application and using SQLite database. I can do every CRUD operation with Entity Framework, but in some specific cases I have to use raw SQL queries, and sometimes it's not returning what I need.
Here is a sample code:
using (var db = new DbContext(AppIO.DatabaseFilePath)) {
var key = 12;
string sql = $"SELECT COUNT(*) FROM SomeTable WHERE SomeField={key}";
var result = db.Database.ExecuteSqlCommand(sql);
}
I simplified the example. Here the result, what I got is -1. I copied the sql string value (after it's built) and executed in SQLiteStuido on the same database and it returned the correct value.
The DatabaseFilePath is correct. The connection is set correctly. I'm checking the same databases (in code and in SQLiteStudio). Any other idea?
Try this:
var result = db.Database.SqlQuery<int>(sql).First();
You have to call SqlQuery method and not ExecuteSqlCommand method. Since SqlQuery returns an IEnumerable you have to call Single. This is a the way to retreive scalar values from a query.
using (var db = new DbContext(AppIO.DatabaseFilePath)) {
var key = 12;
string sql = $"SELECT COUNT(*) FROM SomeTable WHERE SomeField={key}";
var result = db.Database.SqlQuery<int>(sql).Single();
}
Since EF Core does not currently execute group by queries in the database, I'm using the following query to get the job done (InvoiceQuery is a DbQuery type):
long merchantId = 177;
var invTokens = new List<string> {"qasas", "efsac"};
string inClause = string.Join(",", invTokens);
string query = $#"SELECT i.Token, i.Balance, COALESCE(SUM(i.Amount), 0) AS ProcessingAmount
FROM inv.Invoices i
WHERE i.Token IN ({inClause})
AND i.MerchantId = {merchantId}
AND i.Status = {(int)InvoiceStatus.Scheduled}
GROUP BY i.Token, i.Balance";
return await dbContext.InvoicesQuery.FromSql(query).ToListAsync();
The above code runs perfect but I don't like it because it gives me a warning of a 'Possible SQL injection vulnerability'.. I know I can use a #pragma to suppress this warning (that's what I'm currently using).
I have tried passing the query and provide the params to the FromSql method, and that works but the parameter that goes into the IN clause is not getting correctly mapped..
Tried using $"'{string.Join("', '", invTokens)}'" but it didn't work.
Any help would be appreciated
Since EF Core 2.0, you can use a FormattableString, where you can use string interpolation for queries.
String interpolation in FromSql and ExecuteSqlCommand
FormattableString Class
In EF Core 2.0 we added special support for interpolated strings to our two primary APIs that accept raw SQL strings: FromSql and ExecuteSqlCommand. This new support allows C# string interpolation to be used in a 'safe' manner.
The example from the docs:
var city = "London";
var contactTitle = "Sales Representative";
using (var context = CreateContext())
{
context.Set<Customer>()
.FromSql($#"
SELECT *
FROM ""Customers""
WHERE ""City"" = {city} AND
""ContactTitle"" = {contactTitle}")
.ToArray();
}
This will produce:
#p0='London' (Size = 4000)
#p1='Sales Representative' (Size = 4000)
SELECT *
FROM ""Customers""
WHERE ""City"" = #p0
AND ""ContactTitle"" = #p1
I am executing this query in SQLite for .NET
string SQL = "SELECT * FROM EmployeeStats where TransactionDateTime = '$date'";
using (SQLiteCommand command = new SQLiteCommand(SQL, DbConnection as SQLiteConnection))
{
command.Parameters.AddWithValue("$date", DateTime.Now.ToString(DateFormatterString));
SQLiteDataReader reader = command.ExecuteReader();
string query = command.CommandText;
foreach (SQLiteParameter p in command.Parameters)
{
query = query.Replace(p.ParameterName, p.Value.ToString());
}
}
The reader returns zero rows.
I am generating the sql query using the foreach loop. If i execute the query generated by this loop manually in the database then it returns the correct rows.
Currently the Database column for the dates is set to TEXT. However i have also tried DATE and TIMESTAMP and changed the parameter addition to this. But still the same problem:
command.Parameters.Add("$date", DbType.DateTime).Value = DateTime.Now.ToString(DateFormatterString);
See this images showing it working fine when executed manually.
EDIT. I simplified the example.
SELECT * FROM EmployeeStats where TransactionDateTime = '$date'
should be
SELECT * FROM EmployeeStats where TransactionDateTime = $date
or #date, or :date, depending on the SQL variant; worst case, it could also be
SELECT * FROM EmployeeStats where TransactionDateTime = ?
if named parameters aren't a "thing" on that provider.
'$date' is the string literal consisting of 5 characters: $, d, a, t, e - not the value of the parameter called date
I am getting data from database in following way:
result = (from d in context.FTDocuments
join f in context.FTDocFlags on d.ID equals f.DocID into fgrp
from x in fgrp.DefaultIfEmpty()
where d.LevelID == levelID && x.UserID == userID && d.Status.Equals(DocumentStatus.NEW)
select new Entities.Document
{
ArrivalDate = d.ArrivalDate.Value,
BundleReference = d.BundleRef,
CreatedDate = d.CreatedDate,
CustomerID = d.CustomerID,
DocType = d.DocType.Value,
GuidID = d.DocGuid,
ID = d.ID,
LastExportID = d.LastExpID,
LevelID = d.LevelID,
ProfileID = d.ProfileID,
ScanDate = d.ScanDate.Value,
ScanOperator = d.ScanOperator,
SenderEmail = d.SenderEmail,
Status = d.Status,
VerifyOperator = d.VerOperator,
FlagNo = x == null ? 0 : x.FlagNo,
FlagUserID = x == null ? 0 : x.UserID
}).ToList();
Now, I am try to achieve this by using sql queries:
var test = context.Database.SqlQuery<string>("select *from FTDocument d left outer join FTDocFlag f on d.ID=f.DocID").ToList();
But get the following error:
The data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types
Is it possible to use complex queries like above?
I use EF 6.0.
your query does not return a single string. Use like:
var test = context.Database.SqlQuery<Entities.Document>("...");
try this
const string query = #"select *from FTDocument d left outer join FTDocFlag f on d.ID=f.DocID";
var test = context.Database.SqlQuery<Entity>(query).ToList();
In my answer I have assumed EntitityFramework (ObjectContext) first, but then I have added the code for DbContext as well.
To check out the example below, you can use LinqPad and add your Entity Framework DLL by using EntitityFramework(ObjectContext) via Add connection. Specify connection properties and close the connection dialog. Then select the connection and run the example:
void Main()
{
var context=this; // requires that you selected an EF ObjectContext connection
var q=context.ExecuteStoreQuery<FTDocument>(
"SELECT * FROM FTDocument WHERE ID = #p0", 1);
q.ToList().Dump();
}
It will accept all kind of SQL queries, and you can use parameters like #p0, #p1 etc and simply append them comma-separated when you invoke the function ExecuteStoreQuery. The result will be returned as List<FTDocument>. To convert it to List<string> you need to specify which database field you want to return - or you create a comma-separated list of field values in each row, for example:
q.Select(s=>s.ID+", "+s.GuidID+", "+s.DocType).ToList().Dump();
The same example, but this time with EntityFramework (DbContext):
Add your Entity Framework DLL by using EntitityFramework(DbContext V4/V5/V6) via Add connection. Specify connection properties (don't forget to specify the AppConfig file) and close the connection dialog. Then select the connection and run the example:
void Main()
{
var context=this; // requires that you selected an EF DBContext (V4/5/6) connection
var q=context.Database.SqlQuery<FTDocument>(
"SELECT * FROM FTDocument WHERE ID = #p0", 1);
q.ToList().Dump();
}
Tip: Before you close the connection dialog, click Test. It will save you a headache later if you know the connection succeeds. For all those who want to try it out with a different EF project with your own database, here is a quick tutorial how to create it. Then simply replace FTDocument in the example above by a different table of your choice (in the SQL string and inside the brackets <...> of course).
I am trying to perform a dapper query like this:
string query = "select * from MyTable where someNumber in #Nums;";
...
connection.Query<ReturnObj>(query, new {Nums = nums})
And I am getting a MySql syntax error if nums is empty. It looks like Dapper changes the query to look like this: WHERE 1 = 0) so I am guessing it the left ( is missing, which is causing the syntax error. Yes, I realize I could just check if the collection is empty before executing the query, but I would rather not if I don't have to.
This is a bug in Dapper where it creates a SQL statement that is invalid for MySQL Server 5.6 (and earlier).
Workarounds:
Upgrade to MySQL Server 5.7 (which accepts the SQL Dapper generates and returns the expected results)
As you said, check if the collection is empty before executing the query
A variant of checking if the collection is empty (that can be useful if you have a complex query, NOT IN, etc.):
var numsSql = nums.Any() ? "#Nums" : "(select null)";
var query = $"select * from MyTable where someNumber in {numsSql};";
conn.Query(query, new { Nums });