Optional parameter with setParameterList in HQL - nhibernate

I have a query with optional parameter
"SELECT ClinicId,Name from Clinic where :ClinicIds is NULL OR ClinicId IN :ClinicIds"
List<int> ClinicIds = null;
I'm passing the parameter as following
q.SetParameterList("ClinicIds", ClinicIds);
Because ClinicId is an optional parameter. If I pass null to SetParameterList I'm getting exception. Any idea how I can pass an optional parameter(null value) to SetParameterList.
Thanks

Rather than using HQL, use a Criteria query. Its designed to be more programmatic than HQL, in that you use straight Java code to assemble your query, which enables you to use if-then logic. So, instead of either concatenating HQL, or having two different HQL queries which you need to independently maintain, you have one Criteria query which accounts for both situations. Example:
//SELECT ClinicId,Name from Clinic where :ClinicIds is NULL OR ClinicId IN :ClinicIds
Criteria criteria = getSession().createCriteria(Clinic.class);
if(ClinicIds == null) {
criteria.add(Restrictions.eq("ClinicId", null));
} else {
criteria.add(Restrictions.or(
Restrictions.eq("ClinicId", null),
criteria.add(Restrictions.in("ClinicId", ClinicIds));
)
);
}
return criteria.list();

You can't. That would generate invalid SQL.
You need to change the HQL depending on whether there are ClinicIds.

I did it like:
"SELECT ClinicId,Name from Clinic where -1 in (:ClinicIds) OR ClinicId IN :ClinicIds"
and
if(ClinicIds == null or ClinicIds.isEmpty()){
ClinicIds = new List<int>();
ClinicIds.add(-1);
}
q.SetParameterList("ClinicIds", ClinicIds);
just a trick

Related

How can I use a method to add a filter on a property in Entity Framework Core?

I need to conditionally add a filter to particular dates in a query. There are common preconditions and the filter will be the same. Therefore I would like the common code to be in a method which can perform these checks and then have the consumer pass in the property which the filter should be applied to (could be applied to multiple).
Here is a simplified version of my code.
var query = dbContext.Documents.AsQueryable();
query = FilterDocumentsByDate(query, x => x.CreatedDate);
query = FilterDocumentsByDate(query, x => x.SubmittedDate);
private IQueryable<Document> FilterDocumentsByDate(IQueryable<Document> query, Func<Document, DateTime> propertyToSearch)
{
query = query.Where(x => propertyToSearch(x).Year > 2000);
return query;
}
When I look at the query in SQL profiler, I can see that the query is missing the WHERE clause (so all documents are being retrieved and the filter is being done in memory). If I copy/paste the code inline for both dates (instead of calling the method twice) then the WHERE clause for the both dates are included in the query.
Is there no way to add a WHERE condition to an IQueryable by passing a property in a Func which can be properly translated to SQL by Entity Framework?
EF is unable to understand your query, so it breaks and executes WHERE clause in memory.
The solution is creating dynamic expressions.
var query = dbContext.Documents.AsQueryable();
query = FilterDocumentsByDate(query, x => x.CreatedDate.Year);
query = FilterDocumentsByDate(query, x => x.SubmittedDate.Year);
private IQueryable<Document> FilterDocumentsByDate(IQueryable<Document> query, Expression<Func<Document, int>> expression)
{
var parameter = expression.Parameters.FirstOrDefault();
Expression comparisonExpression = Expression.Equal(expression.Body, Expression.Constant(2000));
Expression<Func<Document, bool>> exp = Expression.Lambda<Func<Document, bool>>(comparisonExpression, parameter);
query = query.Where(exp);
return query;
}
I am sorry, I haven't run this myself, but this should create WHERE statement. Let me know how it goes.

Entity Framework Query using Contains with mulitple options

Using entity framework to return a list of people where the forename contains text in a string array.
Let's say:
string[] search = new string[] { "bert", "rob" };
and query
dataContext.People.Where(w => search.Any(a => w.Forename.Contains(a)));
This compiles and works BUT the process is actually calling all records from the database and then performing my where clause on the returned data. This makes sense.
Is there a way to rewrite the query so the where clause is generated in SQL?
I assume that dataContext.People is an IQueryable from the DbSet and that there is no materialization instruction involved such as ToList() or AsEnumerable().
the answer is here: http://www.albahari.com/nutshell/predicatebuilder.aspx
in your case:
var predicate = PredicateBuilder.False<People>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Forename.Contains (temp));
}
dataContext.People.Where (predicate);
One way to do this is to use SqlQuery and perform and actual SQL query.
dataContext.Database.SqlQuery<People>("SELECT Forename, Lastname FROM myTable WHERE Forename LIKE '%bert%' or Forename LIKE '%rob%'");

LinqToSql: ExecuteQuery an ExecuteQuery-result?

I use this code to get all customers from my database...
Dim customerResult = db.ExecuteQuery(Of VIEW_customers)("SELECT * FROM TOPL_Customers").ToList
Now that I have all customers fetched from the database, I want to run a "filter" query against customerResult - how do I do that?
Was hoping for something like this...
Dim filterResult = customerResult.ExecuteQuery(Of VIEW_customers)("SELECT * WHERE active=1").ToList
Any suggestions? I don't want to query the database twice.
I need to use a string as a search query because it's dynamic.
Thanks
Try this:
var filteredResult = from a in customerResult
//Add suitable filter condition based on column values
where a.Active == 1
select a;
I'm assuming here that VIEW_customers represents your model for the result set.
You can use Expressions to build up a series of filters and then join them together to create a single, dynamic where clause to run against your result set.
Expression<Func<VIEW_customers, bool>> predicate1 = x => x.someField == 'something';
Expression<Func<VIEW_customers, bool>> predicate2 = x => x.otherField == 'something else';
Then you can join them together with .And or .Or as appropriate:
Expression<Func<VIEW_customers, bool>> combinedPredicate = predicate1.And(predicate2);
You can chain any number of these together; it's boolean logic (basically think of each additional clause as being wrapped by a set of parenthesis). When you're ready to consume the predicate, compile it and run it like a normal .Where clause.
Func<VIEW_customers, bool> compiledPredicate = combinedPredicate.Compile();
var results = customerResult.Where(compiledPredicate);

How to build the correct LINQ query (generate OR instead of AND)

I need some help in building the correct query. I have an Employees table. I need to get a list of all employees, that EENO (Employee ID) contains a string from a supplied array of partial Employee IDs.
When I use this code
// IEnumerable<string> employeeIds is a collection of partial Employee IDs
IQueryable<Employee> query = Employees;
foreach (string id in employeeIds)
{
query = query.Where(e => e.EENO.Contains(id));
}
return query;
I will get something like:
SELECT *
FROM Employees
WHERE EENO LIKE '%1111111%'
AND EENO LIKE '%2222222%'
AND EENO LIKE '%3333333%'
AND EENO LIKE '%4444444%'
Which doesn't make sense.
I need "OR" instead of "AND" in resulting SQL.
Thank you!
UPDATE
This code I wrote using PredicateBuilder works perfectly when I need to include these employees.
var predicate = PredicateBuilder.False<Employee>();
foreach (string id in employeeIds)
{
var temp = id;
predicate = predicate.Or(e => e.EENO.Contains(temp));
}
var query = Employees.Where(predicate);
Now, I need to write an opposite code, to exclude these employees,
here it is but it is not working: the generated SQL is totally weird.
var predicate = PredicateBuilder.False<Employee>();
foreach (string id in employeeIds)
{
var temp = id;
predicate = predicate.And(e => !e.EENO.Contains(temp)); // changed to "And" and "!"
}
var query = Employees.Where(predicate);
return query;
It's supposed to generate SQL Where clause like this one:
WHERE EENO NOT LIKE '%11111%'
AND NOT LIKE '%22222%'
AND NOT LIKE '%33333%'
But it's not happening
The SQL generated is this: http://i.imgur.com/9MDP7.png
Any help is appreciated. Thanks.
Instead of the foreach, just build the IQueryable once:
query = query.Where(e => employeeIds.Contains(e.EENO));
I'd take a look at http://www.albahari.com/nutshell/predicatebuilder.aspx. This has a great way of building Or queries, and is written by the guy that wrote LinqPad. The above link also has examples of usage.
I believe you can use Any():
var query = Employees.Where(emp => employeeIds.Any(id => id.Contains(emp.EENO)));
If you don't want to use a predicate builder, then the only other option is to UNION each of the collections together on an intermediate query:
// IEnumerable<string> employeeIds is a collection of partial Employee IDs
IQueryable<Employee> query = Enumerable.Empty<Employee>().AsQueryable();
foreach (string id in employeeIds)
{
string tempID = id;
query = query.Union(Employees.Where(e => e.EENO.Contains(tempID));
}
return query;
Also keep in mind that closure rules are going to break your predicate and only end up filtering on your last criteria. That's why I have the tempID variable inside the foreach loop.
EDIT: So here's the compendium of all the issues you've run across:
Generate ORs instead of ANDS
Done, using PredicateBuilder.
Only last predicate is being applied
Addressed by assigning a temp variable in your inner loop (due to closure rules)
Exclusion predicates not working
You need to start with the correct base case. When you use ORs, you need to make sure you start with the false case first, that way you only include records where AT LEAST ONE predicate matches (otherwise doesn't return anything). The reason for this is that the base case should just get ignored for purposes of evaluation. In other words false || predicate1 || predicate2 || ... really is just predicate1 || predicate2 || ... because you're looking for at least one true in your list of predicates (and you just need a base to build on). The opposite applies to the AND case. You start with true so that it gets "ignored" for purposes of evaluation, but you still need a base case. In other words: true && predicate1 && ... is the same as predicate1 && .... Hope that addresses your last issue.

multiple parameter "IN" prepared statement

I was trying to figure out how can I set multiple parameters for the IN clause in my SQL query using PreparedStatement.
For example in this SQL statement, I'll be having indefinite number of ?.
select * from ifs_db where img_hub = ? and country IN (multiple ?)
I've read about this in
PreparedStatement IN clause alternatives?
However I can't figure it out how to apply it to my SQL statement above.
There's not a standard way to handle this.
In SQL Server, you can use a table-valued parameter in a stored procedure and pass the countries in a table and use it in a join.
I've also seen cases where a comma-separated list is passed in and then parsed into a table by a function and then used in a join.
If your countries are standard ISO codes in a delimited list like '#US#UK#DE#NL#', you can use a rather simplistic construct like:
select * from ifs_db where img_hub = ? and ? LIKE '%#' + country + '#%'
Sormula will work for any data type (even custom types). This example uses int's for simplicity.
ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);
// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);
ArrayListSelectOperation<Inventory> operation =
new ArrayListSelectOperation<Inventory>(inventoryTable, "partNumberIn");
// show results
for (Inventory inventory: operation.selectAll(partNumbers))
System.out.println(inventory.getPartNumber());
You could use setArray method as mentioned in the javadoc below:
http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html#setArray(int, java.sql.Array)
Code:
PreparedStatement statement = connection.prepareStatement("Select * from test where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"AA1", "BB2","CC3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();