Attached is my current patch.  Some minor things, but the basic approach
is there.

The approach is to add an ExpressionTranslator type (with one current
method, OuterExpression()) which is invoked by SqlBuilder before the
expression is converted into a SQL string, thus allowing vendor-specific
code to manipulate the underlying expression tree (and remove the
OrderBy clauses for Count expressions under SQL Server).

To do so, I made most of the DbLinq.Data.Linq.Sugar.Expressions.* types
public (so that the vendor code could use them).

OK to commit?

Side question: why the preference to interfaces over classes?  FxDG
suggests preferring classes over interfaces for versioning reasons (you
can add methods to classes w/o breaking derived types, which isn't true
with interfaces).  I added a GetTranslator() method to SqlProvider (and
not ISqlProvider) for precisely this reason, as if I were to instead
change ISqlProvider I'd have to change every vendor implementation,
instead of just the ones that matter.

I'd actually prefer to just drop the ISqlProvider interface and use
SqlProvider directly.

Thanks,
 - Jon

On Wed, 2009-05-06 at 15:50 +0200, Pascal Craponne wrote:

> You can probably add such pre-SQL optimizations to
> DbLinq.Data.Linq.Sugar.Implementation.PrequelAnalyzer.
> 
> This class is (poorly at the moment) used to change expressions before
> the SQL generation. This is the place where you could remove an
> OrderBy expression if it precedes a Count, in the case of specific
> databases (with information added to ISqlProvider).
> But this probably won't be obvious, since it probably requires a lot
> of preliminary changes, such as:
> - ISqlProvider to tell if it likes special things such as ordered
> counts
> - Expression analysis, to understand that we're using an order before
> a count (and all possible variants).
> 
> 
> I know it probably doesn't help much more, but I still hope it does
> "unhelp" less :)
> 
> 
> On Wed, May 6, 2009 at 15:16, Jonathan Pryor <[email protected]> wrote:
> 
>         Sorry to ask the obvious, but where is Expression optimization
>         done?  Any types/methods to refer me to?
>         
>         Thanks,
>         - Jon
>         
>         
>         
>         
>         On Wed, 2009-05-06 at 14:14 +0200, Pascal Craponne wrote:
>         
>         > This could probably be done during Expression optimization,
>         > then. I suggest adding some features to IVendor or
>         > ISqlProvider.
>         > (my suggestions may become less accurate, since I haven't
>         > working on DbLinq code for a while)
>         > 
>         > On Wed, May 6, 2009 at 14:03, Jonathan Pryor
>         > <[email protected]> wrote:
>         > 
>         >         I expect no 'ORDER BY' in that case, i.e. 'SELECT
>         >         COUNT(*) FROM [people]' (as that doesn't generate an
>         >         error).  (Alternatively, find some other formulation
>         >         so that SQL Server doesn't reject the 'ORDER BY',
>         >         but I haven't been able to find such a formulation.)
>         >         
>         >         - Jon 
>         >         
>         >         
>         >         
>         >         On Wed, 2009-05-06 at 09:43 +0200, Pascal Craponne
>         >         wrote:
>         >         
>         >         > What SQL statement do you expect in such a case?
>         >         > 
>         >         > On Wed, May 6, 2009 at 06:25, Jonathan Pryor
>         >         > <[email protected]> wrote:
>         >         > 
>         >         >         How is the SQL generated for the .Count()
>         >         >         extension method?
>         >         >         
>         >         >         The current bug I'm seeing is that for the
>         >         >         C# code: 
>         >         >         
>         >         >                 int count =
>         >         >                     (from p in Context.GetTable<Person>()
>         >         >                      orderby p.LastName
>         >         >                      select p)
>         >         >                     .Count();
>         >         >         
>         >         >         the following SQL is generated for
>         >         >         Microsoft SQL Server: 
>         >         >         
>         >         >                 SELECT COUNT(*)
>         >         >                 FROM [people]
>         >         >                 ORDER BY [last_name]
>         >         >         
>         >         >         SQL Server doesn't like this SQL because
>         >         >         of the 'ORDER BY' clause.  After a
>         >         >         slightly more than cursory perusal, I'm
>         >         >         not sure why the 'ORDER BY' is being
>         >         >         generated here, nor am I sure how to fix
>         >         >         this.
>         >         >         
>         >         >         Thoughts?
>         >         >         - Jon
>         >         >         
>         >         >         
>         >         >         
>         >         >         
>         >         >         
>         >         > 
>         >         > 
>         >         > 
>         >         > 
>         >         
>         >         
>         >         
>         >         
>         > 
>         > 
>         > 
>         > 
>         > 
>         
>         
>         
>         
>         
> 
> 
> 
> 
> 
> > 

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"DbLinq" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/dblinq?hl=en
-~----------~----~----~----~------~----~------~--~---

Index: src/DbLinq.SqlServer/Test/MsSqlDataContextTest.cs
===================================================================
--- src/DbLinq.SqlServer/Test/MsSqlDataContextTest.cs	(revision 1065)
+++ src/DbLinq.SqlServer/Test/MsSqlDataContextTest.cs	(working copy)
@@ -28,6 +28,7 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.IO;
 using System.Linq;
 using System.Reflection;
 
@@ -88,6 +89,38 @@
                 "ORDER BY [__ROW_NUMBER]",
                 Environment.NewLine, firstName, lastName, skip, take);
         }
+
+        [Test]
+        public void Count()
+        {
+            var oldLog = Context.Log;
+            var log = new StringWriter();
+            try
+            {
+                Context.Log = log;
+                (from p in Context.GetTable<Person>()
+                     orderby p.LastName
+                     select p)
+                    .Count();
+            }
+            catch (NotSupportedException)
+            {
+                Console.WriteLine("# logfile=\n{0}", log.ToString());
+                var expected = string.Format("SELECT COUNT(*){0}" +
+                    "FROM [people]{0}" +
+                    "--",
+                    Environment.NewLine);
+                Assert.IsTrue(log.ToString().Contains(expected));
+            }
+            catch (Exception e)
+            {
+                Assert.Fail("# ExecuteCommand: Got exception {0}", e.ToString());
+            }
+            finally
+            {
+                Context.Log = oldLog;
+            }
+        }
     }
 }
 
Index: src/DbLinq.SqlServer/SqlServerExpressionTranslator.cs
===================================================================
--- src/DbLinq.SqlServer/SqlServerExpressionTranslator.cs	(revision 0)
+++ src/DbLinq.SqlServer/SqlServerExpressionTranslator.cs	(revision 0)
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+#if MONO_STRICT
+using System.Data.Linq.Sugar.Expressions;
+#else
+using DbLinq.Data.Linq.Sugar.Expressions;
+#endif
+
+namespace DbLinq.SqlServer
+{
+    class SqlServerExpressionTranslator : ExpressionTranslator
+    {
+        public override SelectExpression OuterExpression(SelectExpression e)
+        {
+            // Check for (from f in foo orderby f.Field select f).Count() trees
+            // SQL Server doesn't support 'ORDER BY' for 'SELECT COUNT(*)'.
+            if (e.Operands.Select(o => o as SpecialExpression)
+                    .Where(o => o != null)
+                    .Where(s => s.SpecialNodeType == SpecialExpressionType.Count)
+                    .Any())
+            {
+                e.OrderBy.Clear();
+            }
+            return e;
+        }
+    }
+}

Property changes on: src/DbLinq.SqlServer/SqlServerExpressionTranslator.cs
___________________________________________________________________
Added: svn:executable
   + *

Index: src/DbLinq.SqlServer/SqlServerSqlProvider.cs
===================================================================
--- src/DbLinq.SqlServer/SqlServerSqlProvider.cs	(revision 1063)
+++ src/DbLinq.SqlServer/SqlServerSqlProvider.cs	(working copy)
@@ -25,16 +25,21 @@
 #endregion
 
 using System;
+using System.Diagnostics;
 using System.Linq;
+using System.Linq.Expressions;
 using System.Collections.Generic;
 
 using DbLinq.Vendor.Implementation;
 
 #if MONO_STRICT
 using System.Data.Linq.Sql;
+using System.Data.Linq.Sugar;
 using System.Data.Linq.Sugar.Expressions;
 #else
 using DbLinq.Data.Linq.Sql;
+using DbLinq.Data.Linq.Sugar;
+using DbLinq.Data.Linq.Sugar.Implementation;
 using DbLinq.Data.Linq.Sugar.Expressions;
 #endif
 
@@ -47,6 +52,11 @@
 #endif
  class SqlServerSqlProvider : SqlProvider
     {
+        public override ExpressionTranslator GetTranslator()
+        {
+            return new SqlServerExpressionTranslator();
+        }
+
         protected override char SafeNameStartQuote { get { return '['; } }
         protected override char SafeNameEndQuote { get { return ']'; } }
 
Index: src/DbLinq.SqlServer/DbLinq.SqlServer.csproj
===================================================================
--- src/DbLinq.SqlServer/DbLinq.SqlServer.csproj	(revision 1063)
+++ src/DbLinq.SqlServer/DbLinq.SqlServer.csproj	(working copy)
@@ -51,6 +51,7 @@
       <Link>Properties\DbLinq.ProductInfo.cs</Link>
     </Compile>
     <Compile Include="SqlServerDataContext.cs" />
+    <Compile Include="SqlServerExpressionTranslator.cs" />
     <Compile Include="SqlServerSqlProvider.cs" />
     <Compile Include="SqlServerTypeConversions.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
Index: src/DbLinq/Test/Providers/DataContextTestBase.cs
===================================================================
--- src/DbLinq/Test/Providers/DataContextTestBase.cs	(revision 1063)
+++ src/DbLinq/Test/Providers/DataContextTestBase.cs	(working copy)
@@ -48,6 +48,10 @@
     {
         DataContext context;
 
+        protected DataContext Context {
+            get { return context; }
+        }
+
         [SetUp]
         public void SetUp()
         {
Index: src/DbLinq/DbLinq.csproj
===================================================================
--- src/DbLinq/DbLinq.csproj	(revision 1062)
+++ src/DbLinq/DbLinq.csproj	(working copy)
@@ -144,6 +144,7 @@
     <Compile Include="Data\Linq\Sugar\ExpressionMutator\Implementation\UnaryExpressionMutator.cs" />
     <Compile Include="Data\Linq\Sugar\ExpressionPrecedence.cs" />
     <Compile Include="Data\Linq\Sugar\ExpressionQuery.cs" />
+    <Compile Include="Data\Linq\Sugar\Expressions\ExpressionTranslator.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\ColumnExpression.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\EntitySetExpression.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\ObjectInputParameterExpression.cs" />
Index: src/DbLinq/Data/Linq/Sugar/Expressions/GroupExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/GroupExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/GroupExpression.cs	(working copy)
@@ -44,7 +44,10 @@
     /// It is usually transparent, except for return value, where it mutates the type to IGrouping
     /// </summary>
     [DebuggerDisplay("GroupExpression")]
-    internal class GroupExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class GroupExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.Group;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/StartIndexOffsetExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/StartIndexOffsetExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/StartIndexOffsetExpression.cs	(working copy)
@@ -10,7 +10,10 @@
 namespace DbLinq.Data.Linq.Sugar.Expressions
 #endif
 {
-    internal class StartIndexOffsetExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class StartIndexOffsetExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.StartIndexOffset;
         public bool StartsAtOne{get; private set;}
Index: src/DbLinq/Data/Linq/Sugar/Expressions/ColumnExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/ColumnExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/ColumnExpression.cs	(working copy)
@@ -44,7 +44,10 @@
     /// Describes a column, related to a table
     /// </summary>
     [DebuggerDisplay("ColumnExpression {Table.Name} (as {Table.Alias}).{Name}")]
-    internal class ColumnExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class ColumnExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.Column;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/MetaTableExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/MetaTableExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/MetaTableExpression.cs	(working copy)
@@ -43,7 +43,10 @@
     /// <summary>
     /// A MetaTablePiece contains aliases for tables (used on joins)
     /// </summary>
-    internal class MetaTableExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class MetaTableExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.MetaTable;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/InputParameterExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/InputParameterExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/InputParameterExpression.cs	(working copy)
@@ -40,7 +40,10 @@
 #endif
 {
     [DebuggerDisplay("InputParameterExpression (current value={GetValue()})")]
-    internal class InputParameterExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class InputParameterExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.InputParameter;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/IMutableExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/IMutableExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/IMutableExpression.cs	(working copy)
@@ -37,7 +37,10 @@
     /// Allows an Expression to enumerator its Operands and be mutated, ie changing its operands
     /// Depending on the Expression type (such as System.Linq.Expressions), a new copy may be returned
     /// </summary>
-    internal interface IMutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    interface IMutableExpression
     {
         /// <summary>
         /// Represents Expression operands, ie anything that is an expression
Index: src/DbLinq/Data/Linq/Sugar/Expressions/OrderByExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/OrderByExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/OrderByExpression.cs	(working copy)
@@ -35,7 +35,10 @@
     /// <summary>
     /// Represents a ORDER column to be sorted on
     /// </summary>
-    internal class OrderByExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class OrderByExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.OrderBy;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/CustomExpressionType.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/CustomExpressionType.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/CustomExpressionType.cs	(working copy)
@@ -33,7 +33,10 @@
     /// <summary>
     /// Those types are required by DbLinq internal expressions
     /// </summary>
-    internal enum CustomExpressionType
+#if !MONO_STRICT
+    public
+#endif
+    enum CustomExpressionType
     {
         Scope = 1000,
         MetaTable = 1010,
Index: src/DbLinq/Data/Linq/Sugar/Expressions/SelectExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/SelectExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/SelectExpression.cs	(working copy)
@@ -43,7 +43,10 @@
     /// ScopeExpression describes a selection.
     /// It can be present at top-level or as subexpressions
     /// </summary>
-    internal class SelectExpression : OperandsMutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class SelectExpression : OperandsMutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.Scope;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/SpecialExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/SpecialExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/SpecialExpression.cs	(working copy)
@@ -53,7 +53,10 @@
     /// Holds new expression types (sql related), all well as their operands
     /// </summary>
     [DebuggerDisplay("SpecialExpression {SpecialNodeType}")]
-    internal class SpecialExpression : OperandsMutableExpression, IExecutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class SpecialExpression : OperandsMutableExpression, IExecutableExpression
     {
         public SpecialExpressionType SpecialNodeType { get { return (SpecialExpressionType)NodeType; } }
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/OperandsMutableExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/OperandsMutableExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/OperandsMutableExpression.cs	(working copy)
@@ -35,7 +35,10 @@
 namespace DbLinq.Data.Linq.Sugar.Expressions
 #endif
 {
-    internal abstract class OperandsMutableExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    abstract class OperandsMutableExpression : MutableExpression
     {
         protected OperandsMutableExpression(ExpressionType expressionType, Type type, IList<Expression> operands)
             : base(expressionType, type)
Index: src/DbLinq/Data/Linq/Sugar/Expressions/TableExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/TableExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/TableExpression.cs	(working copy)
@@ -44,7 +44,10 @@
     /// Different joins specify different tables
     /// </summary>
     [DebuggerDisplay("TableExpression {Name} (as {Alias})")]
-    internal class TableExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class TableExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.Table;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/ObjectInputParameterExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/ObjectInputParameterExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/ObjectInputParameterExpression.cs	(working copy)
@@ -40,7 +40,10 @@
 #endif
 {
     [DebuggerDisplay("ObjectInputParameterExpression")]
-    internal class ObjectInputParameterExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class ObjectInputParameterExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.ObjectInputParameter;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/ObjectOutputParameterExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/ObjectOutputParameterExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/ObjectOutputParameterExpression.cs	(working copy)
@@ -40,7 +40,10 @@
 #endif
 {
     [DebuggerDisplay("ObjectOutputParameterExpression")]
-    internal class ObjectOutputParameterExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class ObjectOutputParameterExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.ObjectOutputParameter;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/EntitySetExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/EntitySetExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/EntitySetExpression.cs	(working copy)
@@ -45,7 +45,10 @@
     /// It is usually transparent, except for return value, where it mutates the type to IGrouping
     /// </summary>
     [DebuggerDisplay("EntityExpression: {TableExpression.Type}")]
-    internal class EntitySetExpression : MutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    class EntitySetExpression : MutableExpression
     {
         public const ExpressionType ExpressionType = (ExpressionType)CustomExpressionType.EntitySet;
 
Index: src/DbLinq/Data/Linq/Sugar/Expressions/TableJoinType.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/TableJoinType.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/TableJoinType.cs	(working copy)
@@ -33,7 +33,10 @@
 #endif
 {
     [Flags]
-    internal enum TableJoinType
+#if !MONO_STRICT
+    public
+#endif
+    enum TableJoinType
     {
         /// <summary>
         /// No join specified
Index: src/DbLinq/Data/Linq/Sugar/Expressions/MutableExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/MutableExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/MutableExpression.cs	(working copy)
@@ -39,7 +39,10 @@
 namespace DbLinq.Data.Linq.Sugar.Expressions
 #endif
 {
-    internal abstract class MutableExpression : Expression, IMutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    abstract class MutableExpression : Expression, IMutableExpression
     {
         protected MutableExpression(ExpressionType expressionType, Type type)
             : base(expressionType, type)
Index: src/DbLinq/Data/Linq/Sugar/Expressions/ExpressionTranslator.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/ExpressionTranslator.cs	(revision 0)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/ExpressionTranslator.cs	(revision 0)
@@ -0,0 +1,65 @@
+using System;
+using System.Linq.Expressions;
+
+#if MONO_STRICT
+namespace System.Data.Linq.Sugar.Expressions
+#else
+namespace DbLinq.Data.Linq.Sugar.Expressions
+#endif
+{
+    /// <summary>
+    ///  Permits translation of expressions for specific database vendors
+    /// </summary>
+    /// <remarks>
+    ///  <para>
+    ///   Not all databases are equal, and some databases have SQL constraints 
+    ///   that are not shared by others.  Some of these SQL dialect constraints
+    ///   can be handled by <see cref="DbLinq.Vendor.ISqlProvider" />, but
+    ///   in some circumstances that's too late in the game.
+    ///  </para>
+    ///  <para>
+    ///   Case in point: for 
+    ///   <c>(from p in people orderby p.LastName select p).Count()</c>
+    ///   is translated into e.g. 
+    ///   <c>SELECT COUNT(*) FROM People ORDER BY LastName</c>.  However, this 
+    ///   is invalid for Microsoft SQL Server (the <c>ORDER BY</c> cannot be
+    ///   present), and by the time <c>ISqlProvider</c> is being used, there's
+    ///   no easy way to remove the OrderBy sequence.
+    ///  </para>
+    ///  <para>
+    ///   The <c>ExpressionTranslator</c> type allows vendor code to manipulate
+    ///   the expression tree <i>before</i> SQL generation, thus allowing 
+    ///   otherwise invalid expressions to be removed prior to the generation 
+    ///   phase.
+    ///  </para>
+    /// </remarks>
+#if !MONO_STRICT
+    public
+#endif
+    class ExpressionTranslator
+    {
+        /// <summary>
+        ///  Translate the entire (outermost) expression.
+        /// </summary>
+        /// <param name="e">
+        ///  A <see cref="SelectExpression" /> containing the expression to 
+        ///  translate.
+        /// </param>
+        /// <returns>
+        ///  The <see cref="SelectExpression" /> to use for SQL generation.
+        /// </returns>
+        /// <remarks>
+        ///  <para>
+        ///   Derived classes can override this method to manipulate the 
+        ///   entire expression prior to SQL generation.
+        ///  </para>
+        ///  <para>
+        ///   The default implementation returns <c>e</c> unchanged.
+        ///  </para>
+        /// </remarks>
+        public virtual SelectExpression OuterExpression(SelectExpression e)
+        {
+            return e;
+        }
+    }
+}

Property changes on: src/DbLinq/Data/Linq/Sugar/Expressions/ExpressionTranslator.cs
___________________________________________________________________
Added: svn:executable
   + *

Index: src/DbLinq/Data/Linq/Sugar/Expressions/IExecutableExpression.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Expressions/IExecutableExpression.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Expressions/IExecutableExpression.cs	(working copy)
@@ -30,7 +30,10 @@
 namespace DbLinq.Data.Linq.Sugar.Expressions
 #endif
 {
-    internal interface IExecutableExpression
+#if !MONO_STRICT
+    public
+#endif
+    interface IExecutableExpression
     {
         /// <summary>
         /// Evaluates the expression value
Index: src/DbLinq/Data/Linq/Sugar/Implementation/SqlBuilder.cs
===================================================================
--- src/DbLinq/Data/Linq/Sugar/Implementation/SqlBuilder.cs	(revision 1062)
+++ src/DbLinq/Data/Linq/Sugar/Implementation/SqlBuilder.cs	(working copy)
@@ -107,6 +107,9 @@
         /// <returns></returns>
         public SqlStatement Build(SelectExpression selectExpression, QueryContext queryContext)
         {
+            var translator = GetTranslator(queryContext.DataContext.Vendor.SqlProvider);
+            selectExpression = translator.OuterExpression(selectExpression);
+
             // A scope usually has:
             // - a SELECT: the operation creating a CLR object with data coming from SQL tier
             // - a FROM: list of tables
@@ -223,6 +226,14 @@
             return sqlProvider.GetLiteral(expression.NodeType, literalOperands);
         }
 
+        private Expressions.ExpressionTranslator GetTranslator(DbLinq.Vendor.ISqlProvider provider)
+        {
+            var p = provider as DbLinq.Vendor.Implementation.SqlProvider;
+            if (p != null)
+                return p.GetTranslator();
+            return new ExpressionTranslator();
+        }
+
         /// <summary>
         /// Determines if a SQL conversion is required
         /// </summary>
Index: src/DbLinq/Properties/AssemblyInfo.cs
===================================================================
--- src/DbLinq/Properties/AssemblyInfo.cs	(revision 1062)
+++ src/DbLinq/Properties/AssemblyInfo.cs	(working copy)
@@ -104,7 +104,6 @@
 + "962a12b830c2a70eb70ec77823eb5750e5bdef9e01d097c30b5c5463c3d07d3472b58e4c02f279"
 + "2309259f")]
 
-
 [assembly: InternalsVisibleTo("DbLinq.Sqlite, PublicKey="
 + "0024000004800000940000000602000000240000525341310004000001000100c5753d8c47f400"
 + "83f549016a5711238ac8ec297605abccd3dc4b6d0f280b4764eb2cc58ec4e37831edad7e7a07b8"
Index: src/DbLinq/Vendor/Implementation/SqlProvider.cs
===================================================================
--- src/DbLinq/Vendor/Implementation/SqlProvider.cs	(revision 1062)
+++ src/DbLinq/Vendor/Implementation/SqlProvider.cs	(working copy)
@@ -50,6 +50,10 @@
 #endif
  class SqlProvider : ISqlProvider
     {
+        public virtual ExpressionTranslator GetTranslator()
+        {
+            return new ExpressionTranslator();
+        }
 
         /// <summary>
         /// Builds an insert clause
Index: src/DbLinq/System.Data.Linq.csproj
===================================================================
--- src/DbLinq/System.Data.Linq.csproj	(revision 1062)
+++ src/DbLinq/System.Data.Linq.csproj	(working copy)
@@ -179,6 +179,7 @@
     <Compile Include="Data\Linq\Sugar\ExpressionQuery.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\ColumnExpression.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\EntitySetExpression.cs" />
+    <Compile Include="Data\Linq\Sugar\Expressions\ExpressionTranslator.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\ObjectInputParameterExpression.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\ObjectOutputParameterExpression.cs" />
     <Compile Include="Data\Linq\Sugar\Expressions\CustomExpressionType.cs" />
@@ -332,6 +333,7 @@
     <Compile Include="Util\Page.cs" />
     <Compile Include="Util\TypeExtensions.cs" />
     <Compile Include="Util\TypeLoader.cs" />
+    <Compile Include="Vendors\DbLinq.SqlServer\SqlServerExpressionTranslator.cs" />
     <Compile Include="Vendor\IDataTableColumn.cs" />
     <Compile Include="Vendor\IDataName.cs" />
     <Compile Include="Vendor\IDataType.cs" />

Reply via email to