NHibernate - LINQ Limitations
i've been using Nhibernate with LINQ a fair bit now and i have a few issues. Say i have the following entities:
public class User
{
public virtual int UserID { get; set; }
public virtual bool IsActive { get; set; }
public virtual bool SomeField { get { return 0; } }
public virtual DateTime DateRe开发者_如何学JAVAgistered { get; set; }
public virtual IList<Membership> Membership { get; set; }
public virtual Membership ValidMembership { get { return Membership.FirstOrDefault(m => m.IsValid); } }
}
public class User2
{
public virtual int UserID { get; set; }
public virtual int MembershipID { get; set; }
}
public class Membership
{
public virtual int MembershipID { get; set; }
public virtual bool IsValid { get; set; }
}
Now if i run the following query:
var users = service.Linq<User>()
.Where(u => u.IsActive) // This would work
.Where(u => u.SomeField > 0) // This would fail (i know i can map these using formulas but this is here to illustrate)
.Where(u => u.Membership.Any(m => m.IsValid)) // This would work
.Where(u => u.ValidMembership != null) // This would fail
.Where(u => u.DateRegistered > DateTime.UtcNow.AddDays(-1)) // This would work
.Where(u => u.DateRegistered.AddDays(1) > DateTime.UtcNow) // This would fail
.Select(u => new User2 { UserID = u.UserID }) // This would work
.Select(u => u.UserID) // This would work
.Select(u => new { UserID = u.UserID }) // This would fail
.Select(u => new User2 { UserID = u.UserID, MembershipID = u.Membership.Any(m => m.IsValid) ? u.Membership.Any(m => m.IsValid).First().MembershipID : 0 }); // This would fail
I've added a comment next to each one to indicated whether they would work or fail. Those are the scenarios i can think of at the moment. I've managed to overcome these issues by converting the data to list before it has to do anything too fancy. This obviously has an impact on performance. I was wondering whether future versions of the LINQ provider for NHibernate will support these? Also does anyone know how the entity framework would handle these scenarios. I'd imagine the entity framework would be an improvement but i don't want to jump ship if the same problems exist.
Appreciate your feedback. Thanks
NHibernate 3 supports more constructs than the contrib provider that you are using (Beta1 was just released, final version is expected before the end of the year)
However, as others pointed out, some constructs are hard (or impossible) to parse, while others require very specific code to translate the expression trees to SQL.
Fortunately, the new provider is also extensible, which means you can add your own db logic for methods of your own, or that are not supported out of the box.
This code shouldn't even compile. User.SomeField is a boolean property but you're trying to return 0 from the getter? SomeField and ValidMemberships shoudn't even be virtual because they are field that wouldn't even be managed by NHibernate.
This is an addition to the answer of Diego Mijelshon
- Calculated properties can never be parsed by a Linq provider out of the box, because method bodies can not be converted into an expression tree.
Some or maybe even all not implemented issues are implemented in the current Linq provider.
Where(u => u.SomeField > 0) // Calculated property SomeField
Where(u => u.ValidMembership != null) // Calculated property ValidMembership
Where(u => u.DateRegistered.AddDays(1) > DateTime.UtcNow) // The method DateTime.AddDays is not implemented for this side of the date comparison operator greater than >
Select(u => new { UserID = u.UserID }) // Creating anonymous objects is not implemented
Select(u => new User2 { UserID = u.UserID, MembershipID = u.Membership.Any(m => m.IsValid) ? u.Membership.Any(m => m.IsValid).First().MembershipID : 0 }); // Ternary operator not implemented
Entity Framework will fail in the same cases as NHibernate (at least for your examples). Remember, Linq uses Deferred loading for Where()-operations - and everything in Linq2SQL (Entity Framework included) and Linq2NHibernate needs to translate to SQL in defered loading. Method calls cannot be converted to SQL - there is no representation of the method in SQL - and that is why it would fail.
When you ToList() - you force the previous Linq-statements to evaluate (to a database-call) and then working forwards you are working on an in-memory represenation allowing you to use the full Linq2Object Expression-trees (which have the possibility of fancy method-calls etc.)
As for your projections - I wouldn't use Linq2NHibernate for those - but instead use the Projections built into 'standard' NHibernate.
精彩评论