An interessing approach would probably be to create an Ado Provider able to
translate and route the requests created against sqlserver for the other
vendors...

This would work untill in the System.Data.Linq.DataContext code is not
coupled with SQL server interfaces or CLR types (which I cannot know).

I know it's a bit like entering from the ass hole (and reparsing the created
SQL to a syntatic tree and back to vendors' sql, but this could be cached as
well), but it would have some advantages.


First, on a Microsoft environment, it would be probably easier to get all
the unit test pass on all vendors.
Second, it would probably save almost all of the current DbLinq code, just
adding the new RoutingAdoProvider's classes.
Third, it would make the mono's System.Data.Linq.DataContext development
easier: it shouldn't produce the sql server code, just use directly the
SqlProvider of the proper vendor, keeping the duplicated syntax tree parsing
and sql creation only to Microsoft environments.


Would this way be praticable?


Giacomo



On Wed, Mar 25, 2009 at 10:21 PM, Jonathan Pryor <[email protected]> wrote:

>  On Wed, 2009-03-25 at 21:13 +0100, Giacomo Tesio wrote:
>
> Thanks for the explain Jon!
>
> Ok... now I'm wondering about:
> - if the System.Data.Linq.DataContext would be such a base class, does we
> still need a Table<T> (when running in microsoft environment, obviously Mono
> ones still need it)?
>
>
> So let's take a step back to further explain the problem, as Table<T> is a
> *really* big problem.
>
> Most people will be using generated code, as produced by sqlmetal or
> DbMetal.  This produces such wonderful code as:
>
> class Northwind : DataContext {
>   public Table<Category> Categories { get { return GetTable<Category>(); } }
>   // ...
> }
>
>  So to do *anything* LINQ-ish, you need DataContext.GetTable<T>().
>
> Next, what does Table<T> do?  For DbLinq, it ties into 
> QueryProvider<T>(internal), and delegates many calls to (non-virtual)
> DataContext members (e.g. Table<T>.Attach(T) calls
> DataContext.RegisterUpdate()).
>
> So, for perfectly innocuous code like db.Categories.Attach(new Category
> {...}), if we inherit from .NET's System.Data.Linq, we will get 
> *no*notification that the Attach() has occurred and that we need to add this 
> new
> value.
>
> FAIL.
>
> It gets worse.  If we provide a shadowing DataContext.GetTable<T>() method
> (as would be required, as GetTable<T>() isn't virtual), we *could* return
> a new Table<T> instance.  However, (1) Table<T> has an internal
> constructor, (2) Table<T> is sealed, and (3) Table<T> has no virtual
> methods.  So even if we could create a new Table<T> instance (which we
> can't), we couldn't customize the behavior at all.
>
> Which is a long-winded way of saying that there's no way of reusing .NET's
> Table<T> with our DataContext.  The types are intimately tied, and if we
> ever want to support entity tracking (which we do), we need to provide our
> own Table<T> type.
>
> For the same reasons, we also need to provide our own ChangeSet and
> DataLoadOptions types..
>
> Where we can reuse .NET types, we already do, e.g.
> ChangeConflictCollection and IMultipleResults.
>
> So when it comes down to it, we can't reuse most of .NET's DataContext
> members, DataContext has no virtual members, and it can't be made to work.
>
> It's also a bad idea for OOP reasons, as DbLinq's DataContext *IS NOT A*.NET
> DataContext (and can't be, due to the lack of virtuals).
>
>  Actually I've to say that what I'd like to depend on microsoft code to
> allow code depending on the System.Data.Linq.DataContext to be injected with
> DbLinq code without too much refactoring.
>
> I don't understand what you mean by this.  How would we "inject" DbLinq
> code?
>
> What I'm saing is to remove some code from the DbLinq.Data.Linq.DataContext
> by deriving it from the System.Data.Linq.DataContext: both the DbLinq
> version of the System.Data.Linq.DataContext and the linq to sql one should
> have the same signature (and the same satellite classes).
> So by changing the reference of a project we would get the microsoft one or
> the our.
>
> That's already possible now: change the assembly reference from
> System.Data.Linq.dll to DbLinq.dll, and change 'using System.Data.Linq;'
> to 'using DbLinq.Data.Linq', and that should work.
>
> Even with your approach, this would be required (unless you wanted DbLinq's
> DataContext to also be in the System.Data.Linq namespace, which is just *
> begging* for trouble).
>
> So from what I can see, your proposal would offer no benefits over what is
> currently being done.
>
> We would really need to override such non virtual methods? Or we need to
> have such methods on our datacontext becouse it doesn't extend the
> System.Data.Linq.DataContext?
>
> What you're missing is the overall picture of what Linq-to-SQL does.
>
> In a nutshell, Linq-to-SQL takes a linq expression
>
> var pens = from p in db.Products
>     where p.ProductName == "Pen"
>     select p;
>
>  And translates this at runtime into the equivalent of the SQL 'select *
> from Products where ProductName = "Pen"'.
>
> This sounds simple, but it isn't, as each vendor has a slightly different
> dialect of SQL.  Consequently, DbLinq needs to be *intimately* involved in
> the SQL generation process, with access to all expression trees, etc.,  in
> order to generate the resulting SQL.
>
> Why the intimacy?  Through normal LINQ, the above code is translated into:
>
> db.Products.Where(p => p.ProductName == "Pen").Select(p => p);
>
>  Since we're dealing with IQueryable<T> and not IEnumerable<T>, this
> actually calls e.g. System.Linq.Queryable.Select(), which for Mono is:
>
> public static IQueryable<TResult> Select<TSource, TResult> (this 
> IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
> {
>       Check.SourceAndSelector (source, selector);
>       return source.Provider.CreateQuery<TResult> (
>               StaticCall (
>                       MakeGeneric (MethodBase.GetCurrentMethod (), typeof 
> (TSource), typeof (TResult)),
>                       source.Expression,
>                       Expression.Quote (selector)));
> }
>
>  Note, specifically, the source.Provider reference.  For the above, 
> sourcewould be
> db.Products, which is Table<Product>, thus source.Provider would be
> Table<T>.Provider, which is explicitly implemented, and will refer to a
> type internal to System.Data.Linq.
>
> Thus, the linchpin of the whole process, the IQueryable interface, is
> buried deep within Linq to SQL with no way to intervene and customize its
> behavior.
>
> *This* is why the lack of virtual members in DataContext (and everything
> else) is a killer: without any virtual members, without *any* way to be
> involved in the IQueryable<T> to SQL conversion process, there is *no way*to 
> generate custom SQL for specific databases.  And without custom SQL, you
> don't have support for databases other than SQL Server.  And without such
> support...what's the point?
>
> - 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
-~----------~----~----~----~------~----~------~--~---

Reply via email to