Get generic list by criteria - fluent-nhibernate

Let's say i have following tables in database:
I want to get a list of all the children with a specific dadId and also i want to re-use this method for other criterias.
Is it something like this that i would use ?
public IList<T> FindBy(Expression<Func<T, bool>> expression)
{
return Session.CreateCriteria<T>()
//add restriction
.List<T>();
}

No.
list<T> may return list<Object[]> depending on the Criteria you have.
You can write a wrapper but this is a lot of Overhead.

public IList<T> GetAll<T>()
where T : class
{
return _session.CreateCriteria<T>().List<T>();
}
Works fine for me! Vote!
Similar answer here - Why can't I use generics with CreateCriteria in NHibernate?
Any reason why i didnt get a vote on this? this code works for me and it appears to be exactly that the user is looking for. Did i miss something?

Related

Mapping of Interface is Not Supported, But Linq-Sql Object Already Implements Property

So, I created a DataContext (Linq-Sql) in VS from an existing database. It has a table called Users, thus I have a User object. In particular, I want to focus on the UserID and Username properties.
Now, I have an interface:
interface IUser
{
int Id { get; }
string Username { get; }
}
I want to create a partial User class and implement IUser. The reason for this is so that I can treat any User as an IUser in many places and not be concerned about the precise User class:
public partial class User : IUser
{
public int Id
{
get { return UserID; }
}
}
I don't implement the Username get property because I know that the entity object already implements it.
When I have a query like dc.Users.SingleOrDefault(p => p.Id == 5); I know that it's an error because it'll translate that call to an SQL statement and it's going to try to find the Id column, which doesn't exist - UserID exists. So I understand this mapping issue.
When I query dc.Users.SingleOrDefault(p => p.Username == "admin"), it also throws an error, BUT Username IS indeed an existing column in the database, so my impression is that no custom/additional mapping needs to take place. What am I missing?
Can someone point me to a good source on how to combat Linq vs. partial classes implement a custom interface?
Update Question:
Before I try it, does anyone know if "rigging" the datacontext.designer.cs file with our custom interfaces (to implement to the classes themselves instead of in a separate partial class file) will work? Is there a consequence of doing this?
I've come across this before using Generics and LINQ, and the way I solved it was to change p.Id == 5 to p.Id.Equals(5) and LINQ was able to map the query.
In regards to rigging autogenerated code, I have done this in my projects, the only annoyance is having to type all the interfaces again if you regenerate your DBML file. I looked in to dynamically adding interfaces to classes and found this SO post, but I haven't tried it out yet:
What is the nicest way to dynamically implement an interface in C#?
Either way, re-typing is a much better trade off for us right now as we've been able to remove a lot of duplication in our implementation code with this method.
Unfortunately I'm not experienced enough with LINQ or .NET to explain why Equals() works when == does not :)

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();

Using NHibernate Collection Filters with DDD collections

I am trying to map a domain model in NHibernate. The domain model is implemented with what I think is DDD style. The mapping works mostly but then when I try to use a collection filter on an a collection I get an exception which says: The collection was unreferenced.
I know the problem comes from how I've implemented the collection. My question: Is it possible to use collection filters in nHibernate on collections implemented this way or should I just forget it, i.e. nHibernate cannot work with this.
The code is as follows:
Person
{
IList<Address> _addresses = new List<Address>();
public string FirstName {get; set;}
...
public void addAddress(Address address)
{
// ... do some checks or validation
_addresses.Add(address);
}
public void removeAddress(Address address) {...}
public ReadOnlyCollection<Address> Addresses
{
get { return new ReadOnlyCollection<Address>(_addresses); }
}
}
The main issue is that I don't want to expose the internal addresses collection publicly.
Every other thing works, I use the field.camelcase-underscore access so nHibernate interacts directly with the field. I've been working through the Hibernate in Action book, an now I'm in chapter 7 where it deals with collection filters.
Is there any way around this. I've got it to work by exposing the internal collection like this:
public ReadOnlyCollection<Address> Addresses
{
get { return _addresses; }
}
but I really dont want to do this.
Help would really be appreciated.
Jide
If I recall correctly - NHibernate filter works as additional clause in sql queries to reduce returned rows from db.
My question to You is - why do You need that?
I mean - how much addresses one person might have? 1? 5? 10?
About collection isolation...
I myself just accept it as a sacrifice for NHibernate (just like argument-less ctor's and "virtual`ity") and use exposed IList everywhere (with private setters) just to reduce technical complexity. Their contents surely can be modified from outside, but I just don't do that.
It's more important to keep code easily understandable than making it super safe. Safety will follow.

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.

Complex Searching with NHibernate

I am curious about what methods do you use for complex searching with NHibernate ?
I am using Ayende's
What is yours ?
Thanks for your advices and answers.
If we have a complex dynamic search, we will usually construct a SearchParameter object and then pass that into a method that will build our criteria for us.
For example, if we were searching for a person we might have a search object that looks like this:
public class PersonSearchParameters
{
public string FirstName {get; set;}
public string LastName {get; set;}
public ICriteria GetSearchCriteria()
{
DetachedCriteria query = DetachedCriteria.For(typeof (Person));
//Add query parameters
Return query;
}
}
Then for each type of search, we'll be able to create the single criteria from the class, or we could have multple search parameter classes and chain them together
We use HQL, but we're still trying to wrap our heads around the Criteria API for complex queries. We have to manage a lot of duplication when using HQL.
I use pretty much Ayende's too jsut a bit more complex, what do you want to do that you cant do with that?
Basically what we added is that we have an interface where we define all the fields where we want to search and we call this when we are about to make the search which means that we can easily change what we are searching for.
Also we are using Active Record in the project ( on top of Hibernate) and tis pretty cool, loads of tasks gets simplified , thou the lack of docs does hurt sometimes
Cheer