NHibernate ISQLQuery SetParameter issue - nhibernate

This is probably fairly straightforward but i can't seem to find a reasonable explanation in any documentation.
I'm trying to use an NHibernate.ISQLQuery and using SetResultTransformer() to return a custom set of results from a custom SQL query. Like so:
public virtual IList<T> GetSQLObject<T>(string sql, IDbParameter[] parameters = null)
{
ISQLQuery qry = _sess.CreateSQLQuery(sql);
qry.SetResultTransformer(Transformers.AliasToBean(typeof(T)));
if (parameters != null) {
foreach (IDbParameter parameter in parameters) {
qry.SetParameter(parameter.Name, parameter.Value);
}
}
return qry.List<T>();
}
From looking at the examples, it seems that in the sql query I have to use parameters in the format :param1 instead of #param1 as I would in a standard SQL query. If i use the latter syntax in the query, it throws an error at qry.SetParameter().
Is there a reason why ISQLQuery/NHibernate requires them in this format and won't work with the normal syntax?

SQL Server uses #param, but not every other database does. For example, MySQL uses ?param
NHibernate allows you to swap out 1 database implementation for another with little to no reworking of your DAL. It sets the parameters based on the database you configured when you setup the NH Configuration.
Edit: Also I think :param came about from Hibernate being targeted at Oracle when it was initially developed, since Oracle uses :param

Phil has answered the "why"; so perhaps I can recommend a "how"; why not just add a new extension method to the IDbParameter type (something like .GetNHibernateName() ) that will return the parameter name with the "#" replaced with a ":"; that should be trivial to implement.

Related

How to run a function using a nhibernate criteria?

We have a search routine that uses criteria to build SQL query (because its restrictions added dynamically).
In a particular case (a very complicated case) we need to search over a table-valued function.(our model object is mapped to the function).
The result would be something like this :
SELECT count(*) FROM dbo.GetSubStaffsLetterInstances(#staffId) WHERE LetterNumber="1234";
The problem is I don't know how to pass #staffId to my criteria(I tried adding an Eq restrictions without success since restrictions are working on properties)
I know I can add a parameter to an IQuery but I don't know how I can do it using an ICriteria object.
If I understand your question completely, you can resort back to standard SQL:-
var sql = "SELECT count(*) FROM dbo.GetSubStaffsLetterInstances(:staffId)
WHERE LetterNumber=:letterNum";
var count = session.CreateSqlQuery(sql)
.setInt32("staffId", 12345)
.setString("letternum", "A1")
.UniqueResult<int>();
or try .UniqueResult<long>(); as I can't remember which one HQL returns

NHibernate: DB2400Dialect: Dialect does not support variable limits

I'm working with a S#arp Architecture project that includes some database Tasks that have worked in the past. Specifically:
var principals = _principalTasks.GetAll().AsPagination(page, limit);
where the task is defined as:
public IQueryable<Principal> GetAll()
{
return _principalRepository.FindAll().OrderBy(o => o.PrincipalName.ToLower());
}
This is effectively using NHibernate.Linq.
This is using the DB2400Dialect. Now it throws:
System.NotSupportedException: Dialect does not support variable limits.
at NHibernate.Dialect.Dialect.GetLimitString(SqlString queryString, Nullable`1 offset, Nullable`1 limit, Parameter offsetParameter, Parameter limitParameter)
at NHibernate.Hql.Ast.ANTLR.SqlGenerator.GetSqlStringWithLimitsIfNeeded(QueryWriter queryWriter)
at NHibernate.Hql.Ast.ANTLR.SqlGenerator.EndQuery()
at NHibernate.Hql.Ast.ANTLR.SqlGenerator.selectStatement()
at NHibernate.Hql.Ast.ANTLR.SqlGenerator.statement()
at NHibernate.Hql.Ast.ANTLR.HqlSqlGenerator.Generate()
.
.
.
It looks like the SQLGenerator insists on parameterizing the skip and take parameters which this dialect does not support.
Is there a way around this or is this an NHibernate bug?
EDIT:
BTW, this is the Expression Debug string from the NHibernate.Linq.DefaultQueryProvider call:
.Call System.Linq.Queryable.Take(
.Call System.Linq.Queryable.Skip(
.Call System.Linq.Queryable.OrderBy(
.Constant<NHibernate.Linq.NhQueryable`1[SolutionExample.Domain.Principal]>(NHibernate.Linq.NhQueryable`1[SolutionExample.Domain.Principal]),
'(.Lambda #Lambda1<System.Func`2[SolutionExample.Domain.Principal,System.String]>)),
0),
25)
.Lambda #Lambda1<System.Func`2[SolutionExample.Domain.Principal,System.String]>(SolutionExample.Domain.Principal $o) {
.Call ($o.PrincipalName).ToLower()
}
After much research I've decided that while I could solve this question by either creating my own custom dialect that implements - or extending the existing DB2400Dialect to implement -
public SqlString GetLimitString(SqlString queryString, int? offset, int? limit, Parameter offsetParameter, Parameter limitParameter)
that would be pointless since while the iSeries allows a limit with the
... FETCH FIRST n ROWS ONLY
syntax, it has no equivalent syntax for doing an offset... so, there isn't much point to fixing the broken bits.

Rails and SQL Injection: Is this safe?

#usersfound = User.find_by_sql(["
SELECT * from users where name ## plainto_tsquery('english', ?) LIMIT 20 offset ?
",#query,#offset])
See above, is this safe from sql injection? I am very new to doing direct sql commands on a database in rails. (I am aware there may be other ways of doing this SPECIFIC query, but I am wondering if in general, using find_by_sql and that kind of insertion of vars is safe - I have some difficult queries with subselects and joins that are really possible to do with ActiveRecord.
Thanks.
Yes, that should be safe. If you trace through the code you'll find that your find_by_sql call ends up calling PGconn#send_query_prepared with the bind parameters being carried along as little more than baggage; the send_query_prepared method is just a wrapper for the PQsendQueryPrepared API call in libpq:
static VALUE
pgconn_send_query_prepared(int argc, VALUE *argv, VALUE self)
{
/* ... bunch of boiler plate marshalling stuff ... */
result = PQsendQueryPrepared(conn, StringValuePtr(name), nParams,
(const char * const *)paramValues, paramLengths, paramFormats,
resultFormat);
/* ... */
}
The bind parameters end up in paramValues. So you should be fine unless there are bugs in PostgreSQL's C library prepared statement handling.
Inserting dynamic values into a query using query parameters is safe.
But it depends on whether Rails is "faking" query parameters, and is actually combining #query and #offset into the SQL string before preparing the statement. Then it's only as safe as the implementation of escaping in Rails.

Why doesn't a separately instantiated Func<T,bool> predicate not translate into SQL with Entity Framework?

I have an EF Code First Db context that I'm using to query the database. I noticed some performance issues when passing in queries as Func<Product, bool>s from my Aggregate Repository and on investigating further it turned out that the queries were not being translated into SQL Queries.
After a little more digging I discovered the following.
var results = _context.Products
.Where(p => p.ProductCode.Contains("AAA"))
.Where(p => p.CategoryId == 1)
.ToList();
This works exactly as expected. It generates some parametrized SQL with a Where Clause.
==================================================================
var results2 = _context.Products
.Where(p => p.ProductCode.Contains("AAA") && p.CategoryId == 1)
.ToList();
This also works as expected. It generates the same sql as above
==================================================================
Func<Product, bool> pred = (p => p.ProductCode.Contains("AAA") && p.CategoryId == 1);
var results3 = _context.Products.Where(pred).ToList();
This is broken. It doesn't generate the where clause in the SQL, it returns everything and then filters it in code.
Because in order to translate into SQL, it has to be an Expression<...>, not a Func<...>.
This is done automatically for you by the compiler, and since the overloads on the Linq-to-SQL classes takes expressions, not delegates, the compiler will automagically translate your code (which looks like a lambda or an anonymous method) into an expression object and pass that.
However, if you take care of building the function yourself, the compiler cannot do this, and Linq-to-SQL does not take anonymous methods, it only takes expressions.
What you can do is to execute the parts of your query that you can, and then filter the results through your function, but I would look into just changing the type of your value into an expression instead.
No sooner than I posted this ReSharper helped answer my question by showing me the overload method signature for the Where() extension method.
It takes both Func<T, bool> and Expression<Func<T, bool>>. If your declaring your predicates externally, you must use the Expression variation as the former is not translated into sql.
Here's why the query reads the whole table.
When a Func is used instead of an Expression, the compiler chooses methods on System.Linq.Enumerable - instead of System.Linq.Queryable. The Enumerable methods iterate the source collection (sometimes lazily) while the Queryable methods build up the expression tree.
Since the call to Where isn't part of the expression tree, the sql generator doesn't see it during the query translation.

Is it possible to do Standard Deviation in NHibernate?

Is it possible to calculate Standard Deviation with NHibernate? I'm using the Microsoft SQL Server 2005 Dialect.
I can't find any examples of this anywhere.
Please help.
thanks!
Just wanted to let everyone know how I accomplished this with code that may help others:
When I create my SessionFactory, I simply add a custom SQL Function to the Configuration Object:
Setup:
var configuration = new Configuration();
configuration.AddSqlFunction("stdev", new StandardSQLFunction("stdev", HibernateUtil.Double));
SessionFactory = configuration.Configure().BuildSessionFactory();
Usage:
Then in my data provider I now can call the custom function:
ICriteria criteria = _session.CreateCriteria(typeof(ItemHistory));
criteria.SetProjection(Projections.SqlFunction("stdev", NHibernateUtil.Double, Projections.Property("Speed") ));
IList results = criteria.List();
I'm not that familiar with NHibernate, but using the Java Hibernate you can subclass an existing dialect easily to add extra functions. Something like this might do the trick for you (this is the Java version):
public class MyDialect extends SQLServerDialect
{
public MyDialect()
{
super();
// register extra functions (may need to specify parameter types)
registerFunction( "stdev", new StandardSQLFunction( "stdev" ) );
}
}
STDEV is a native SQL function for SQL Server... can you pass through a native query?
You can use native SQL queries, including aggregate functions, in HQL (Hibernate Query Language). See Chapter 12 in the documentation.
Edited to add: I just read it myself and STDEV is not supported. An alternative that might work is to create a view that includes the calculation and map the view instead of the table. Further Googling leads me to believe that the documentation may be out of date or there are extensions to NHibernate that include STDEV.
I've solved in a more standard way by this code:
sqrt((sum(value*value)/count(value)) - (avg(value) * avg(value)))
In other way, unrolling the formula of the standard deviation using the other aggregate functions that are supported: sum and count.
I've checked against the formula 'std()' in mysql and this works except by the least significant digits (error less of 0.000000001D).