Writing a driver for a non ADO data source.
Hi;
Im trying to write a driver for a non ADO data source - by which I mean I have data, in memory already loaded and the data is exposed simply as IList[T] objects.
In our data store we expose objects of type Cursor[T] this class implements a property "Objects" that implements the IList[T] interface and so obviously can be queried easily.
I can automatically and dynamically create instances of Cursor[T] objects if I need to, but I cant see how to get this to work with the LINQPad driver model.
It seems that the driver model insists on there being a DataServiceContext object that has members of type DataServiceQuery[T] - it also looks as if LINQPad creates and compiles this class itself from metadata based on the schema etc of the DB.
No doubt under-the-hood it scrutinizes this DataServiceContext object at runtime to get at the various DataServiceQuery[T] members and so is then able to query against their sequences.
It's all a bit puzzling and I am wondering how I can get this to work.
DataServiceQuery[T] instances can only be created via the CreateQuery[T] method in DataServiceContext but this requires some form of string URI and this too is confusing me.
I know nothing about DataServiceContext or DataServiceQuery stuff, never used it - and never needed to with LINQ fo Objects either so this is pretty puzzling.
Must we expose DataServiceQuery[T] members or can we just expose something that is IEnumerable[T] and the driver will "know this"?
Thanks for any guidance.
PS: I cant use angle brackets for generics in this editor!
Im trying to write a driver for a non ADO data source - by which I mean I have data, in memory already loaded and the data is exposed simply as IList[T] objects.
In our data store we expose objects of type Cursor[T] this class implements a property "Objects" that implements the IList[T] interface and so obviously can be queried easily.
I can automatically and dynamically create instances of Cursor[T] objects if I need to, but I cant see how to get this to work with the LINQPad driver model.
It seems that the driver model insists on there being a DataServiceContext object that has members of type DataServiceQuery[T] - it also looks as if LINQPad creates and compiles this class itself from metadata based on the schema etc of the DB.
No doubt under-the-hood it scrutinizes this DataServiceContext object at runtime to get at the various DataServiceQuery[T] members and so is then able to query against their sequences.
It's all a bit puzzling and I am wondering how I can get this to work.
DataServiceQuery[T] instances can only be created via the CreateQuery[T] method in DataServiceContext but this requires some form of string URI and this too is confusing me.
I know nothing about DataServiceContext or DataServiceQuery stuff, never used it - and never needed to with LINQ fo Objects either so this is pretty puzzling.
Must we expose DataServiceQuery[T] members or can we just expose something that is IEnumerable[T] and the driver will "know this"?
Thanks for any guidance.
PS: I cant use angle brackets for generics in this editor!
Comments
But there are two new problems now:
1. The assembly that defines my types (for the data) must be signed - it seems so anyway.
2. The build/compile process on the driver seems to need this assembly to be a disk file.
At leatst I'm progressing...
Hugo
Regarding filtering the output, there are a number of ways to do this. It's described fully in the documentation here:
http://www.linqpad.net/extensibility.aspx
Cheers
Joe
Appreciate the info, very helpful - of course the filtering is a non-issue actually - we are using LINQ after all so can render whatever we need, just been a while since I even looked at LINQ and the driver is my main focus.
Now - after some small tweaks (again this is experimental/feasibility stuff here) I am getting this when I try to run a query:
The type or namespace name 'TypedDataContext' does not exist in the namespace 'LINQPad.User'
I have populated the tree view using the metadata in our data source, and enabled the context menu etc.
The node I am testing with is named Watchitems - the IEnumerable member in the data context class is named Watchitems and the generic T is Watchitem.
This is a contrived example/test as I explore the whole driver concept - but I was getting execution when the tree view was based on the Astoria stuff - but now that the tree view is being created for our data we get this runtime error.
Just to explain I have taken the Astoria code sample - which works - and incrementally tweaked it to gain insights/understanding. The version that 'worked' simply had a modified property in the dcDataService class that used our T and returned our data of type T.
I commented out the src code gen stuff and made it a fixed src file so that I could do these things, once all is stable I will write my own DataService class generator - if I need to (it seems I do).
Clearly something happens when a node is clicked and a query runs, and the error "The type or namespace name 'TypedDataContext' does not exist in the namespace 'LINQPad.User' " means something but I have no idea, nor are any exceptions raised under debug.
Any help much appreciated!
Hugo
For the time being forget about how these come to be, assume the driver can peform some data load operation and immediately "see" IEnumerable[T] collections, assume the data store exposes these without any need to work with SQL, O/R mappings or anything like a DataContext.
Does the driver model support this scenario or is it currently expecting to work only with DataContext and SQL like data sources? I'm new to WCF, DataContext and all this - so far as I am understanding things, LINQ too has no direct association with these itself.
I can (if neccesary) contrive a dynamically generated data context class myself but there is no service url or anything in our case so I am unsure of how to force this DataContext model to work with pure in-memory collections.
Thanks
LINQPad's notion of a "typed data context" is any non-abstract class that has properties or fields or methods that the user can query. It does not have to be based on LINQ to SQL's DataContext class, or anything else for that matter. The properties/fields will typically be of type IEnumerable or IQueryable (so the user can run LINQ queries over them), but they don't have to be. For example, you might emit an assembly with the following class: When the user runs a query, LINQPad will subclass this and instantiate the subclass. For instance, suppose the user types this and hits F5:
CustomerNames.Dump();
Bar.Where (b => b > 10).Dump();
DoSomething();
LINQPad will convert this into the following code, compile it, and then run it:
Another question - and I can live with this for time being I think - our datapools are hierarchical - and data is stored in a directory hierarchy, this is quite unlike SQL Server where there is a conceptual level of "tables" in which all data sets reside.
So by way of example, at the root of a datapool there may be two dirs "current_data" and "old_data" and we may have collections of type T in each perhaps named "Customers".
So - again conceptually - we could refer to TWO data collections of the same type "current_data.Customers" and "old_data.Customers".
I may be wrong but this seems to suggest the LINQPad driver model can't work with this as it has a single TypedDataContext class - yet we need two one for collections within "current_data" and one for collections within "old_data".
This would seem to suggest some kind of TypeDataContext that might "look like" this:
public class TypedDataContext { public IEnumerable<string> CustomerNames (string path); public IEnumerable<int> Bar (string path); public void DoSomething() { } }
If this were supported then LINQPad could do as it does now and treat properties as it dos now, but if a clicked name (in this example 'CustomerNames') is found to map to a method not a property - then it could pass the entire tree-view path of the clicked node.
In this case our own logic could then use the path text to ascertain which actual directory we need to query, this is of course just an idea - you may have a way already or may have better ideas for supporting such a hierarchical store.
Thanks again - and back to my beta....
You can re-use the same type name by giving it a prefix or putting it in a different namespace, or putting it in a nested class. To access the "old" data, you could write a separate typed data context and expose it like this: Take a look at how LINQPad handles multiple databases in a single query - it's a similar situation. Create a connection to SQL Server and follow these instructions:
http://www.linqpad.net/FAQ.aspx#cross-database
You can see how it works underneath with .NET Reflector. Press Shift+F1 to reflect the current type/member.
A nested class would be an excellent way of doing this - let me explore these ideas now that you have explained this to me.
Thx
Hugo
A suggestion emerges here too:- what about an ExplorerItem having a "DisplayedName" property - this could default to the current 'Name" property. But if a driver sets this string property then this becomes the text displayed as the node's name.
This will allow a node to have a simple (unqualified) name displayed in the tree - but have it's fully qualified name available in the Name property - which is the name used by LINQPad to resolve the eventual member collection property.
Anyway this was the last issue I need to resolve - I think I can create a solid driver now.
Thanks for help.
Hugo
I can now connect to any datapool and run queries against any data in it, no matter where that data appears in the overall hierachy.
Just need to do a tidy up and a set of formal tests and we're good.
Thanks for your help with all this Joe.
Hugo