Converting this method from IList to IQueryable - nhibernate

Is it possible to convert:
public IList Get()
{
return Session.CreateCriteria(typeof(T)).List();
}
to return IQueryable?
What is the difference between IList and IQueryable?

One of the most important aspects when thinking of SQL and Linq is that returning IList means that the query has been executed. Returning IQueryable keeps open the option of deferring the sql execution later (so you could potentially build on the query outside of this method).
However, this would only be the case with NHibernate when using the Linq to NHibernate provider. The Criteria API is not Linqified for the SQL statements. So realistically in this instance returning IList or IQueryable has no significant difference.

What is possible is to return an IEnumerable like this:
public IEnumerable<T> Get()
{
return Session.CreateCriteria(typeof(T)).Future<T>();
}
This way you can do deferred execution as you do with Linq to SQL.
For more info about the Future method see:
http://ayende.com/Blog/archive/2009/04/27/nhibernate-futures.aspx
You can also simulate the IQueryable by returning the ICriteria interface instead of an IList:
public ICriteria<T> Get()
{
return Session.CreateCriteria(typeof(T));
}
This way you can start building the query outside of your method and finally execute it whenever you want.

Related

EF Core generating bad SQL [duplicate]

I'd like to be able to implement a search method that can take any arbitrary properties of my POCO class as arguments. This works well:
public static IEnumerable<iUser> Search(DataContext context, Func<iUser, bool> predicate)
{
return from i in context.GetTable<iUser>().Where(predicate) select i;
}
but in this case the filtering appears to take place after collecting all the rows in the table.
Is it possible to use Linq to generate an arbitrary query like this without filtering after the sql call? What approaches would you recommend?
Thanks!
LINQ to DB is an Object-Relational Mapper (ORM) that is capable of translating LINQ expressions into SQL. The word "expression" is important here. A Func is not an expression but a delegate, you have to use Expression<Func<>> in LINQ methods for LINQ to DB to be able to translate them. Otherwise the data will be pulled from the database first after which the Func filters them in memory.
So your function should look like:
public static IEnumerable<iUser> Search(DataContext context,
Expression<Func<iUser, bool>> predicate)
{
return context.GetTable<iUser>().Where(predicate);
}
The return type depends on the what you want the caller of this function to be capable of. If you return IQueryable<iUser> the caller will be able to extend the expression by their own expressions. That is, Search(context, somePredicate).Where(...) will be translated into SQL as a whole. Returning IEnumerable will apply any subsequent predicates (either as Func or as Expression) in memory.
Side note, in order to line up with common naming conventions, if iUser is an interface (I have no idea if LINQ to DB supports interfaces) then you should rename it into IUser, otherwise name it User.

NHibernate detached OrderBy

I am using a repository pattern to wrap NHibernate entities. One of the methods is public IList<T> GetAll() which simply returns all items of that entity. The implementation is done in either Criteria or QueryOver.
I would like to overload this method to accept a sorting order, something like this: public IList<T> GetAll(NHOrderFor<T> order) which I could call and fluently define the order for. Is this possible? QueryOver is preferred but not required.
Update
I got a little further ahead. I defined the parameter as Expression<Func<T,object>> path which is what's expected by QueryOver.OrderBy() but the expression is missing the .Asc or .Desc specification that's required to follow.
You can pass in a bool variable to determine if it's asc or desc - the only "tricky" part is that the .Asc and .Desc are properties, so you have to assign them to a result (you don't need to do anything with the result tho - it just returns the same queryover), eg:
public IList<T> GetAll(Expression<Func<T,object>> path, bool ascending) {
if (ascending)
queryOver = queryOver.OrderBy(path).Asc;
else
queryOver = queryOver.OrderBy(path).Desc;
return queryOver.List<T>();
}

What to do when your passing in a collection of zero into a hql request?

I have this
public List<TableA> Get(Guid Id, List<int> listId)
{
const string query = "FROM TableA WHERE MyListColumn in (:listId) AND Id in (:Id)";
return session.CreateQuery(query).SetParameterList("listId", listId).SetParameter("Id",Id).List<TableA>().ToList();
}
I am wondering what to do if keys count equals zero? When using a linq way it would jsut return an empty List of TableAs. The only way I can think of is a "if" statement but I am wondering if there is any other way before I do that since I don't want really logic in my method here as it is a repo method.
if(listId.Count == 0){..}
As I get this error
Server Error in '/' Application. An
empty parameter-list generate wrong
SQL; parameter name 'listId'
I consider an if block proper as it's not "program logic": it's query generation.
That said, i personally change the query signature to a MyListColumn =:id for listId.Count==1 and for listId.Count==0 i don't run the query at all: Why have a round-trip when you know that no results will return?
Based on your other questions regarding this topic, I suggest using QueryOver:
The following query will work with listId == null and listId.Count == 0.
public List<TableA> Get(List<int> listId)
{
return session.QueryOver<TableA>()
.Where(Restrictions.In(
Projections.Property<TableA>(e => e.Id),
listId ?? (new List<int>() { }))).List().ToList();
}
I dropped the Guid Id parameter, since that doesn't make much sense here.
Edit:
Yes, there are several ways in NHibernate to query the database (plain SQL, HQL, ICriteria, QueryOver and NHibernate.Linq). All of these have their strengths and weaknesses and I usually try to use the method best suited for the job.
In this I suggestes QueryOver instead of HQL, because you get full Intellisense and compiler support. Also, QueryOver does not throw an error if the list is empty (it will translate to WHERE 1=0). As Jaguar wrote, in that case you could also return an empty list without going to the DB.
I usually only use HQL when QueryOver or ICriteria can't get me the result I want. The Linq provider still lacks some features, so that will currently only work for rather simple queries.
You can also do the above query with Linq. Here is the NHibernate.Linq version of the code above: (this works in NHibernate 3.1, not tested in previous versions)
List<int> idList = new List<int>(){ 1, 2 };
// if idList is null this will still throw an error
var result = session.Query<TableA>()
.Where(e => idList.Contains(e.Id)).ToList();

Cast an IList to an IList<t> using dynamic instantiation

I am try to convert the following NHibernate query using dyanmic instantiation into an IList<t> rather than an IList.
IList<AllName> allNames =
(IList<AllName>)Session.CreateQuery(
#"select new AllName(
name.NameId, name.FirstName, origin.OriginId, origin.Description,
name.Sex, name.Description, name.SoundEx
) from Name name join name.Origin origin")
.SetFirstResult(skip)
.SetMaxResults(pageSize);
Running this I get the following error:-
Unable to cast object of type
'NHibernate.Impl.QueryImpl' to type
'System.Collections.Generic.IList`1[Domain.Model.Entities.AllName]'.
I know that I can return
IList results = Sesssion.CreateQuery(...
but my service layers expect an
IList<AllName>
How can I achieve this?
One option would be to write an IList<T> implementation which proxies to an IList, casting where appropriate. It shouldn't be too hard to do, albeit somewhat tedious. LINQ will make this slightly easier in terms of implementing IEnumerable<T> etc - you can just call the underlying iterator and call Cast<T>() on it, for example.
Another solution would be to create a new List<T> from the returned list:
IList query = Session.CreateQuery(...);
IList<AllName> allNames = new List<AllName>(query.Cast<AllName>());
EDIT: As Sander so rightly points out, this is what ToList is for:
IList query = Session.CreateQuery(...);
return query.Cast<AllName>().ToList();
EDIT: All of this has been NHibernate-agnostic, as it were - I don't actually know much about NHibernate. Rippo has now found a much simpler answer - call List<AllName> instead (which is an NHibernate method).

How can one delete NHibernate objects using a criteria?

This must be a simple question. Given a criteria, how one deletes the entities satisfying the criteria?
The rationale:
HQL and NH criteria are NHibernate specific constructs and as such they are server side DAL implementation details. I do not want them to "leak" to the client side. So, our client side provides LINQ expressions for the server to process. Up until now the requests where select requests and LINQ to NHibernate dealed with them just fine.
However, there is a need to implement batch delete operation now. As usual, the client side provides a LINQ expression and the server is to delete entities satisfying the expression.
Unfortunately, LINQ to NHibernate is of no help here. The most it can do is translate the given LINQ expression to NHibernate criteria.
Anyway, this is the story. I wish to stress that the client side is unaware of NHibernate at all and I like it to stay this way.
P.S.
I am using NH 2.1
You may use the criteria to select the IDs of your elements, join them in a string and use HQL to delete them?
Something like:
public void Delete(ICriteria criteria, string keyName, string tableName)
{
criteria.setProjection(Projections.Attribute(keyName));
IList<int> itemIds = criteria.List<int>();
string collection = string.Join(",", Array.ConvertAll<int, string>(itemIds, Convert.ToString));
Session.HQL(string.Format("delete from {0} where {1} in ({2})", tableName, keyName, collection);
}
This code was not tested or compiled (in particular I'm not sure of the HQL section), but I think that you got the idea: we don't fetch the whole objects thanks to the projection, but only the indices.
Simply put, up until 2.1.2 you cannot.
However, if you can translate the LINQ expression to HQL (or the ICriteria to HQL) then you can use the overloaded ISession.Delete() method which uses a passed HQL string.
In your repository/dao/persistencemanager/whatever class:
public IEnumerable<T> FindAll(DetachedCriteria criteria)
{
return criteria.GetExecutableCriteria(Session).List<T>();
}
and then
public void Delete(DetachedCriteria criteria)
{
foreach (T entity in FindAll(criteria))
{
Delete(entity);
}
}
See Davy Brion's post Data Access with NHibernate.
Edit:
As far as I know, if you want to use Criteria you need to load the objects and iterate over them to delete them. Alternatively use HQL or pass in the SQL to the session.
I know this is an old question but for argument sake; if one uses repository pattern you can declare a delete method which does the following:
public void Delete(System.Linq.Expressions.Expression<System.Func<TEntity, bool>> predicate)
{
var entities = _session.Query<TEntity>().Where(predicate);
foreach (var entity in entities)
_session.Delete(entity);
}
Note the code is using expressions in order for repository interface to be generic enough so you can also implement a for example Entity Framework repository.