Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expanding properties for an IQueryable query

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.

like image 993
Paul Turner Avatar asked Jul 23 '10 10:07

Paul Turner


People also ask

When should we use IQueryable?

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.

What is the use of IQueryable in C#?

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.

What is the difference between returning IQueryable T vs IEnumerable T?

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.

What is difference between IQueryable and list in C#?

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.


2 Answers

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:

  1. Pragmatic: all your extensions to a POCO should be methods, not properties. That reflects what you are doing: an action that returns something. Hence, use Pony GetNewestPony() as declaration. Same way I solved this in POCOs with GetFullName() which combines first, middle, last name.
  2. Design: it is best to keep your POCOs plain and simple. That way they can be autogenerated and can evolve without hurting anybody. Just gettors/settors and that's it. To solve your issue then: use what C# introduced in 3.0: extension methods (no, extension properties do not exist).
  3. Architecture: it is common to place the DAO layer as a separate layer in a separate asssembly from the Domain layer. That way, both the business logic (getting selections, all, updating, all CRUD etc) can go in its own DAO class. To do this well, you need excessive use and understanding of C# interfaces and generalization. This concept is explained clearly and detailed by Billy McCafferty and later evolved into the now famous S#arp Architecture.

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.

like image 179
Abel Avatar answered Oct 21 '22 15:10

Abel


This won't completely solve your problem but maybe it can help. Using an NHibernate Formula to aid searching

like image 27
adriaanp Avatar answered Oct 21 '22 16:10

adriaanp