I have the following fictional domain classes:
public class Pony
{
public string Name { get; set; }
public DateTime FoundDate { get; set; }
}
public class Person
{
public ICollection<Pony> Ponies { get; private set; }
public Pony NewestPony
{
get
{
return Ponies
.OrderBy(pony => pony.FoundDate)
.FirstOrDefault();
}
}
}
The NewestPony
property encapsulates a domain rule that determines what the newest pony that lucky person has found.
I can use this property using the Enumerable
extension methods:
IEnumerable<Person> people = GetPeople();
people.Where(p => p.NewestPony != null);
This is great.
However, people
is an IQueryable
facade to a SQL Server (NHibernate in this case), the query has to be processed by a query provider and translated into SQL. Because there is no physical "NewestPony" column in the database, the query provider will choke on the query.
To solve this, I can expand the property in the query, replacing it with its get
implementation:
IEnumerable<Person> people = GetPeople();
person.Where(p
=> p.Ponies.OrderBy(pony => pony.FoundDate).FirstOrDefault() != null);
Because the query provider understands the relationships in SQL Server, it can evaluate this query now.
This solution, though workable, does duplicate that rule that was previously encapsulated. I'm looking for a way to avoid this duplication of behaviour, perhaps even a way to expand the property in the query provider to allow it to generate the correct SQL statements.
IQueryable uses Expression objects that result in the query being executed only when the application requests an enumeration. Use IQueryable when you have to run ad-hoc queries against data source like LinQ to SQL Server,Entity framework and other sources which implement IQueryable.
The IQueryable interface inherits the IEnumerable interface so that if it represents a query, the results of that query can be enumerated. Enumeration causes the expression tree associated with an IQueryable object to be executed. The definition of "executing an expression tree" is specific to a query provider.
The major difference between IQueryable and IEnumerable is that IQueryable executes query with filters whereas IEnumerable executes the query first and then it filters the data based on conditions.
IQueryable is useful when we want to iterate a collection of objects which deals with ad-hoc queries against the data source or remote database, like SQL Server. IList is useful when we want to perform any operation like Add, Remove or Get item at specific index position in the collection.
You hit at a typical problem here. There are a few ways of solving this. If you use some automapping, you should exclude that property, if you use normal mapping (HBM files) you can just ignore the property. But with the LINQ statement it may still go wrong as you explained. Here are a few workarounds:
Pony GetNewestPony()
as declaration. Same way I solved this in POCOs with GetFullName()
which combines first, middle, last name.For a quick fix: use nr 1 or 2. For a thorough future-looking fix, change your architecture, otherwise NHibernate will bite you in the back, instead of helping you.
EDIT:
Below, adriaanp hits on a sore point of using extension methods and I believe he's right. Because I almost always use autogenerated DTO's, I want them separated completely from any methods I introduce by hand. If I want them on the object though, instead of as extension methods, Microsoft introduced partial
classes for exactly this purpose (to extend auto-generated classes in the class itself, without loosing roundtrip engineering). But here too: be careful with not making it messy, clear filenames can help a lot.
This won't completely solve your problem but maybe it can help. Using an NHibernate Formula to aid searching
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With