|
From: <ste...@us...> - 2010-06-21 14:57:39
|
Revision: 4993
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4993&view=rev
Author: steverstrong
Date: 2010-06-21 14:57:32 +0000 (Mon, 21 Jun 2010)
Log Message:
-----------
Improved support for projections, can now do things like
from u in users
select SomeCSharpFunction(u.username)
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Linq/Functions/FunctionRegistry.cs
trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs
trunk/nhibernate/src/NHibernate/Linq/Visitors/SelectClauseVisitor.cs
trunk/nhibernate/src/NHibernate.Test/Linq/ProjectionsTests.cs
Modified: trunk/nhibernate/src/NHibernate/Linq/Functions/FunctionRegistry.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Functions/FunctionRegistry.cs 2010-06-19 15:00:19 UTC (rev 4992)
+++ trunk/nhibernate/src/NHibernate/Linq/Functions/FunctionRegistry.cs 2010-06-21 14:57:32 UTC (rev 4993)
@@ -28,56 +28,85 @@
{
public class FunctionRegistry
{
- public static FunctionRegistry Initialise()
- {
- var registry = new FunctionRegistry();
+ public static readonly FunctionRegistry Instance = new FunctionRegistry();
- // TODO - could use reflection here
- registry.Register(new QueryableGenerator());
- registry.Register(new StringGenerator());
- registry.Register(new DateTimeGenerator());
- registry.Register(new ICollectionGenerator());
-
- return registry;
- }
-
private readonly Dictionary<MethodInfo, IHqlGeneratorForMethod> _registeredMethods = new Dictionary<MethodInfo, IHqlGeneratorForMethod>();
private readonly Dictionary<MemberInfo, IHqlGeneratorForProperty> _registeredProperties = new Dictionary<MemberInfo, IHqlGeneratorForProperty>();
private readonly List<IHqlGeneratorForType> _typeGenerators = new List<IHqlGeneratorForType>();
+ private FunctionRegistry()
+ {
+ // TODO - could use reflection here
+ Register(new QueryableGenerator());
+ Register(new StringGenerator());
+ Register(new DateTimeGenerator());
+ Register(new ICollectionGenerator());
+ }
+
public IHqlGeneratorForMethod GetMethodGenerator(MethodInfo method)
{
IHqlGeneratorForMethod methodGenerator;
+ if (!TryGetMethodGenerator(method, out methodGenerator))
+ {
+ throw new NotSupportedException(method.ToString());
+ }
+
+ return methodGenerator;
+ }
+
+ public bool TryGetMethodGenerator(MethodInfo method, out IHqlGeneratorForMethod methodGenerator)
+ {
if (method.IsGenericMethod)
{
method = method.GetGenericMethodDefinition();
}
- if (_registeredMethods.TryGetValue(method, out methodGenerator))
+ if (GetRegisteredMethodGenerator(method, out methodGenerator)) return true;
+
+ // No method generator registered. Look to see if it's a standard LinqExtensionMethod
+ if (GetStandardLinqExtensionMethodGenerator(method, out methodGenerator)) return true;
+
+ // Not that either. Let's query each type generator to see if it can handle it
+ if (GetMethodGeneratorForType(method, out methodGenerator)) return true;
+
+ return false;
+ }
+
+ private bool GetMethodGeneratorForType(MethodInfo method, out IHqlGeneratorForMethod methodGenerator)
+ {
+ methodGenerator = null;
+
+ foreach (var typeGenerator in _typeGenerators.Where(typeGenerator => typeGenerator.SupportsMethod(method)))
{
- return methodGenerator;
+ methodGenerator = typeGenerator.GetMethodGenerator(method);
+ return true;
}
+ return false;
+ }
- // No method generator registered. Look to see if it's a standard LinqExtensionMethod
- var attr = method.GetCustomAttributes(typeof (LinqExtensionMethodAttribute), false);
+ private bool GetStandardLinqExtensionMethodGenerator(MethodInfo method, out IHqlGeneratorForMethod methodGenerator)
+ {
+ methodGenerator = null;
+
+ var attr = method.GetCustomAttributes(typeof(LinqExtensionMethodAttribute), false);
+
if (attr.Length == 1)
{
// It is
- // TODO - cache this? Is it worth it?
- return new HqlGeneratorForExtensionMethod((LinqExtensionMethodAttribute) attr[0], method);
+ methodGenerator = new HqlGeneratorForExtensionMethod((LinqExtensionMethodAttribute)attr[0], method);
+ return true;
}
+ return false;
+ }
- // Not that either. Let's query each type generator to see if it can handle it
- foreach (var typeGenerator in _typeGenerators)
+ private bool GetRegisteredMethodGenerator(MethodInfo method, out IHqlGeneratorForMethod methodGenerator)
+ {
+ if (_registeredMethods.TryGetValue(method, out methodGenerator))
{
- if (typeGenerator.SupportsMethod(method))
- {
- return typeGenerator.GetMethodGenerator(method);
- }
+ return true;
}
-
- throw new NotSupportedException(method.ToString());
+ return false;
}
public IHqlGeneratorForProperty GetPropertyGenerator(MemberInfo member)
Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs 2010-06-19 15:00:19 UTC (rev 4992)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs 2010-06-21 14:57:32 UTC (rev 4993)
@@ -12,7 +12,7 @@
{
private readonly HqlTreeBuilder _hqlTreeBuilder;
private readonly VisitorParameters _parameters;
- static private readonly FunctionRegistry FunctionRegistry = FunctionRegistry.Initialise();
+ static private readonly FunctionRegistry FunctionRegistry = FunctionRegistry.Instance;
public static HqlTreeNode Visit(Expression expression, VisitorParameters parameters)
{
Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/SelectClauseVisitor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/SelectClauseVisitor.cs 2010-06-19 15:00:19 UTC (rev 4992)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/SelectClauseVisitor.cs 2010-06-21 14:57:32 UTC (rev 4993)
@@ -3,12 +3,15 @@
using System.Linq.Expressions;
using NHibernate.Hql.Ast;
using NHibernate.Linq.Expressions;
+using NHibernate.Linq.Functions;
using Remotion.Data.Linq.Parsing;
namespace NHibernate.Linq.Visitors
{
public class SelectClauseVisitor : ExpressionTreeVisitor
{
+ static private readonly FunctionRegistry FunctionRegistry = FunctionRegistry.Instance;
+
private HashSet<Expression> _hqlNodes;
private readonly ParameterExpression _inputParameter;
private readonly VisitorParameters _parameters;
@@ -69,7 +72,24 @@
private static bool CanBeEvaluatedInHqlSelectStatement(Expression expression)
{
- return (expression.NodeType != ExpressionType.MemberInit) && (expression.NodeType != ExpressionType.New);
+ if ((expression.NodeType == ExpressionType.MemberInit) || (expression.NodeType == ExpressionType.New) || (expression.NodeType == ExpressionType.Constant))
+ {
+ // Hql can't do New or Member Init
+ return false;
+ }
+
+ if (expression.NodeType == ExpressionType.Call)
+ {
+ // Depends if it's in the function registry
+ IHqlGeneratorForMethod methodGenerator;
+ if (!FunctionRegistry.TryGetMethodGenerator(((MethodCallExpression) expression).Method, out methodGenerator))
+ {
+ return false;
+ }
+ }
+
+ // Assume all is good
+ return true;
}
private static bool CanBeEvaluatedInHqlStatementShortcut(Expression expression)
Modified: trunk/nhibernate/src/NHibernate.Test/Linq/ProjectionsTests.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/Linq/ProjectionsTests.cs 2010-06-19 15:00:19 UTC (rev 4992)
+++ trunk/nhibernate/src/NHibernate.Test/Linq/ProjectionsTests.cs 2010-06-21 14:57:32 UTC (rev 4993)
@@ -147,5 +147,55 @@
Assert.AreEqual(3, query.Intersect(new[] { "ayende", "rahien", "nhibernate" })
.ToList().Count);
}
+
+ [Test]
+ public void CanCallLocalMethodsInSelect()
+ {
+ var query = (
+ from user in db.Users
+ orderby user.Id
+ select FormatName(user.Name, user.LastLoginDate)
+ ).ToList();
+
+ Assert.AreEqual(3, query.Count);
+ Assert.IsTrue(query[0].StartsWith("User ayende logged in at"));
+ Assert.IsTrue(query[1].StartsWith("User rahien logged in at"));
+ Assert.IsTrue(query[2].StartsWith("User nhibernate logged in at"));
+ }
+
+ [Test]
+ public void CanCallLocalMethodsInAnonymousTypeInSelect()
+ {
+ var query = (
+ from user in db.Users
+ orderby user.Id
+ select new {Title = FormatName(user.Name, user.LastLoginDate)}
+ ).ToList();
+
+ Assert.AreEqual(3, query.Count);
+ Assert.IsTrue(query[0].Title.StartsWith("User ayende logged in at"));
+ Assert.IsTrue(query[1].Title.StartsWith("User rahien logged in at"));
+ Assert.IsTrue(query[2].Title.StartsWith("User nhibernate logged in at"));
+ }
+
+ [Test]
+ public void CanPerformStringOperationsInSelect()
+ {
+ var query = (
+ from user in db.Users
+ orderby user.Id
+ select new { Title = "User " + user.Name + " logged in at " + user.LastLoginDate }
+ ).ToList();
+
+ Assert.AreEqual(3, query.Count);
+ Assert.IsTrue(query[0].Title.StartsWith("User ayende logged in at"));
+ Assert.IsTrue(query[1].Title.StartsWith("User rahien logged in at"));
+ Assert.IsTrue(query[2].Title.StartsWith("User nhibernate logged in at"));
+ }
+
+ private string FormatName(string name, DateTime? lastLoginDate)
+ {
+ return string.Format("User {0} logged in at {1}", name, lastLoginDate);
+ }
}
}
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|