I'm facing a problem in PetaPoco, and I can't figure it out.
I'm using this code :
var db = new OracleConnection(_connectionString);
var query = Sql.Builder;
query.Append("SELECT * FROM City WHERE ID = #0", 1);
return db.Query<City>(query.SQL).ToList();
PetaPoco is not adding the parameter to my sql query.
This is an example from their official website :
var id=123;
var sql=PetaPoco.Sql.Builder
.Append("SELECT * FROM articles")
.Append("WHERE article_id=#0", id);
For you information : I'm using the last version of PetaPoco (5.0.1)
I'd skip Sql.Builder (it's gaining you nothing here), and as a side-note, use db.Fetch as it already returns a List. So:
var query = "SELECT * FROM City WHERE ID = #0";
return db.Fetch<City>(query, 1);
Just query to the Query<> method, not just the sql.
Otherwise you need to pass the Arguments as the second parameter.
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 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).
Exception:
Local variable/parameter ':your-param-name' can only be used within a database procedure.
In MSSQL, the param character is #. In INGRES Database, really is : ? I cannot find official documentation for that...
SELECT * FROM MyIngresTable WHERE MyColumn = :poorParam
C# with Spring.NET AdoTemplate:
IDbParametersBuilder builder = CreateDbParametersBuilder();
builder.Create().Name("poorParam").Type(DbType.Int32).Value(1);
Any help?
Solved!
Just use INTERROGATION without named param, and pass values in order of usage.
string query = "SELECT * FROM MyIngresTable WHERE MyColumn >= ? and OtherColumn = ?";
IDbParametersBuilder builder = CreateDbParametersBuilder();
builder.Create().Type(DbType.Int32).Value(10);
builder.Create().Type(DbType.String).Size(4).Value("test");
IList<YourModelType> data = AdoTemplate.QueryWithRowMapper(
CommandType.Text,
query,
new YourRowMapper<YourModelType>(),
builder.GetParameters()
);
Another tip about Spring.NET AdoTemplate:
using Spring.Data.Common;
using Spring.Data.Generic;
protected virtual IDbParametersBuilder CreateDbParametersBuilder()
{
return new DbParametersBuilder(AdoTemplate.DbProvider);
}
Thanks also, Panagiotis Kanavos.
I am doing a quick CSHTML page for the purpose of testing.
I need to access database based on the id parameter on the URL:
var id = Request.QueryString["id"];
var db = Database.Open("mydatabase_connection");
var query = "select * from myrecord where id = " + id;
var row = db.QuerySingle(query);
//i am able to display the field (called name) of the selected record in the following way:
#row.name
Obviously, the above approach is subject to security attack. I am hoping to retrieve the record the following way:
var query = "select * from myrecord where id=#0";
var row = db.Execute(query, id);
However, I get runtime error when retrieving the field value:
#row.name
What is the correct way of getting the "row" in the second approach?
Thanks and regards.
Database.Execute is for executing a non-query SQL statement and returns the count of records affected by the SQL statement as an Int.
I think the method you want to use really is Database.QuerySingle, which returns an Object.
ie.
var query = "select * from myrecord where id=#0";
var row = db.QuerySingle(query, id);
Razor:
#row.name
As far as safety from SQL injection goes, this approach is safe. You are passing the URL value into your query as a parameter.
The unsafe way to run the query would be with string concatenation:
var query = "select * from myrecord where id=" + id;
Don't do this! It allows for a malicious user to append SQL statements to your query! Always use parameterized queries instead.