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).
Related
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();
While learning to use LINQ in VB.NET, I came across the following:
Dim x As IEnumerable = (some LINQ query)
If you can't instantiate an interface, but only a concrete implementation of it, why is this allowed? Is there some difference between doing
Dim x as (Type) and
Dim x as New (Type)?
Yes, a big difference. In the code you give, you're declaring a variable that will point at some instantiated thing. It doesn't necessarily know what type of thing it is, but it knows it implements IEnumerable. In the code you gave, the actual instantiation as some concrete type (that implements IEnumerable) is handled by LINQ. The part that goes on the right has to result in a concrete instatiation.
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.
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.
I want to run a query against two tables (which happen to be mapped in ActiveRecord). The query returns a result list that cannot be mapped to an ActiveRecord object (as it is custom aggregate information).
For instance
Dim query_str as string = "Select distinct d.ID, (select count(1) as exp from Sales_Leads where date_created <= :todays_date) as NbrLeads from Dealer d"
Dim q As Queries.HqlBasedQuery = New Queries.HqlBasedQuery(GetType(ICollection), query_str)
q.SetParameter("todays_date", DateTime.Today)
Dim i As ICollection = ActiveRecordMediator.ExecuteQuery(q)
What I'm looking for is simple execution of SQL, without an ActiveRecord object returned.
So, ideally, I'd be able to look at i("NbrResults") for each item in the collection.
The error I am getting is:
You have accessed an ActiveRecord
class that wasn't properly
initialized. The only explanation is
that the call to
ActiveRecordStarter.Initialize()
didn't include
System.Collections.ICollection class
Well, this was asked a long time ago but I have a working answer.
public static IList<T> ExecuteQuery<T>(HqlBasedQuery query)
where T : new()
{
query.SetResultTransformer(new NHibernate.Transform.AliasToBeanResultTransformer(typeof(T)));
var results = (ArrayList)ActiveRecordMediator.ExecuteQuery(query);
List<T> list = new List<T>(results.Count);
for (int i = 0; i < results.Count; i++)
{
list.Add((T)results[i]);
}
return list;
}
This will give you back results of type T. Type T can be anything you want. Type T needs a no argument constructor and it needs public fields or properties that match the column names or aliases in the query you build.
We do this all the time. Particularly when you want to use aggregate function in HQL to produce aggregate data.
A companion function will allow you to just pass in your query as a string as well as any positional parameters you might have:
public static IList<T> ExecuteQuery<T, U>(string hqlQuery, params object[] parameters)
where T : new()
{
return ExecuteQuery<T>(new HqlBasedQuery(typeof(U), hqlQuery, parameters));
}
Type U is any type that is a valid ActiveRecord type. It doesn't even have to be one of the types you are referencing. If you want you could replace it will some type you know is gonna be valid int he session and ditch the extra parameter.
Here was my final solution:
Dim query_str As String = "SELECT DISTINCT d.ID, count( l ) from LEAD as l join l.Dealer as d where l.DateCreated >= DATEADD(day, -30, :todays_date) GROUP BY d.ID"
Then obtain the active record session (or NHibernate, still don't even know what is returned here):
Dim sess As ISession = activerecordmediator.GetSessionFactoryHolder().CreateSession(GetType(ActiveRecordBase))
Dim q As NHibernate.IQuery = sess.CreateQuery(query_str)
q.SetParameter("todays_date", DateTime.Today)
Dim i As IList = q.List() ' get results
In the .aspx page, the result can be accessed in a GridView like so:
You're stepping outside the NHibernate paradigm to call down to SQL, which is somewhat against the spirit of ORM. It's not 'bad' per-se, but I'd certainly avoid breaking the abstraction if I could to try to maintain a looser coupling.
You can do what you want with a straight HQL query, which will return a collection of tuples containing the results of your query. I explained how to do this here
Custom query with Castle ActiveRecord
which you might want to have a look at. Even though you must specify a type when you new an HQLBasedQuery, NH is smart enough to know that if you don't select a type instance it should assemble a result-set based on object tuples.
(IMNSHO it's still a bit non-pure - I'd be tempted to try to model this relationship as an object and map it accordingly, but then I'd have to bash the DB into shape to suit the object model and that's not going to fly in all cases.)