Hanalei, Hawaii Tuesday, February 09, 2010

SubSonic 3.0 Preview 2

Over the last week I've made massive improvements and upgrades to SubSonic 3.0 and the templates that it comes with.

Over the last week I've made massive improvements and upgrades to SubSonic 3.0 and the templates that it comes with. I haven't been this obsessive and inspired since I created SubSonic in the first place, and I have to tell you I am more than stoked to see this pull together.

The Foundation
I'll be really concise with this post - there's so much here ... it will be hard but I really want to keep to the point. SubSonic 3.0 is a Layer Cake which is focused on openness and transparency. Here are the layers:

  1. Core: Data Access and SQL Translation. This is the part that executes commands against the database, and translates queries (SubSonic's or Linq's) into SQL.
  2. Surface: Working with your database. This layer "surfaces" your database and implements "IQuerySurface" (I couldn't think of a better name). In short, it exposes every table as IQueryable<T>, every Stored Procedure as an executable method, and also provides you with Aggregation queries (Count, Sum, Min, Max, etc).
  3. Implementation: Your Favorite Data Pattern. This can be Repository, ActiveRecord... whatever. I've created (with this release) an IRepository pattern for you that I think covers 99% of the needs out there (you'll see in a moment) and also allows for some nice testability.

Of these, the only thing that is locked down is part 1 - The Core (as part of our DLL). The rest is built using T4 templates that you add to your project. So if you hear me constantly say "you can change this as you need to in the templates" - well it's because you can. These aren't hard to learn, and are created using plain old C# code so changing/altering as you need should be easy. F-117_Nighthawk_Front

In fact I was explaining to Steve Harman yesterday that if you wanted to create a full-blown Unit Of Work ORM, you could. You could also create an ActiveRecord pattern too. Really - it's that flexible.

The "Surface"
There's probably a better name for this - but it seems to capture what I'm after: a simple, easy to understand way to work with your database. This template (called "QuerySurface" - which used to be "Provider") exposes the tables in your database as IQueryable<T> so you can work against them using Linq:

            Northwind.DB db = new DB();
            var result = from p in db.Products
                         where p.CategoryID==5
                         select p;

            foreach (var item in result) {
                Console.WriteLine(item.ProductName);
            }

I can go off here and show you all the ways to query with Linq. But you've seen em before and hopefully you won't notice the difference with SubSonic. If you've used Linq To Sql, this will look extremely familiar to you. However know that my approach differs from Linq To Sql in that there is no change tracking and need to have a "context". The DB, in this sense, is simply a wrapper.

One thing you might not have seen with Linq To Sql is the ability to run Updates and Inserts, which I've always missed and have now implemented with SubSonic 3.0:

            db.Update<Products>().Set(
                x => x.Discontinued == false, 
                x => x.ReorderLevel == 100)
               .Where(x=>x.Category==5)
               .Execute();
        db.Insert.Into<Region>(x => x.RegionID, x => x.RegionDescription)
          .Values(6, "Hawaii")
          .Execute();

I know a lot of people don't like the Lambda thing - you can still use our old query tool (with structs or strings in place of these lambdas) if you like.

This one's a personal favorite - deleting has become a one-line operation:

            db.Delete<Region>(x => x.RegionID == 6).Execute();

This next thing is what I consider to be a "Killer Feature" - a batch query (or as NHibernate calls it, a "Future" query). One thing that always gets me about ORMs is having to manage the connections that it makes to a database. Wouldn't it be nice to lob a bunch of IQueryable queries into a batch querier and have it make only one call to the DB, in one execution step? If you think so, I have good news for you:

    db.Queue(from p in db.Products where p.CategoryID == 5 select p);
    db.Queue(from p in db.Products where p.CategoryID == 7 select p);
    db.Queue(from p in db.Products where p.CategoryID == 9 select p);
    IDataReader rdr=db.ExecuteBatch();

With this release I've included a new "BatchQuery" class that takes either one of SubSonic's queries or an IQueryable query, parses the SQL and pulls out the parameters into one, big batched SQL statement. It then will execute as a pass-through, or return a multi-result set. Yes, I will do my best to support transactions this way in the future :).

If you don't like working with DataReaders, never fear, one of the new members of SubSonic's Extension classes (DatabaseExtensions) has a new method for IDataReader called "ToList<T>()" - which will take a reader and shove the results into a "List<T>".

So to complete this example (making sure to reference SubSonic.Extension.DatabaseExtensions):

    db.Queue(from p in db.Products where p.CategoryID == 5 select p);
    db.Queue(from p in db.Products where p.CategoryID == 7 select p);
    db.Queue(from p in db.Products where p.CategoryID == 9 select p);

    List<Products> result1 = null;
    List<Products> result2 = null;
    List<Products> result3 = null;


    using (IDataReader rdr = qry.ExecuteReader()) {
        result1 = rdr.ToList<Products>();
        
        if (rdr.NextResult())
            result2 = rdr.ToList<Products>();

        if (rdr.NextResult())
            result3 = rdr.ToList<Products>();

    }

One SQL statement, one execution step, 3 typed lists. I like it :).

Believe it or not you probably shouldn't work "this close" to your database. This is where the 3rd tier comes in.

The Implementation
Hopefully you can see what I'm trying to do here - give YOU the power. By way of example I've created an IRepository<T> interface that's part of SubSonic:

    namespace SubSonic {
        public interface IRepository<T> {
    
            IQueryable<T> GetAll();
            PagedList<T> GetPaged(int pageIndex, int pageSize);
            IQueryable<T> Find(Expression<Func<T, bool>> expression);
            void Add(T item);
            int Update(T item);
            int Delete(T item);
            int Delete(object key);
            int Delete(Expression<Func<T, bool>> expression);
    
        }
    }

One of the templates you'll see in the download below is "Repository.TT", which is an implementation of this interface that you can use right now if you like. One day I might make an ActiveRecord one as well - but for now you can use this code like this:

            NorthwindRepository<Region> repo = new NorthwindRepository<Region>();
            Region r = repo.Find(x => x.RegionDescription == "Hawaii").SingleOrDefault();
            r.RegionDescription = "Hawaii FIVE O";
            repo.Update(r);

Again, one of my very favorites is "Delete":

            NorthwindRepository<Region> repo = new NorthwindRepository<Region>);
            repo.Delete(x=>x.RegionID==6);

You can also use the item, or a primary key:

            repo.Delete(6);
            //OR
            Region r=repo.Find(x=>x.RegionID==6).SingleOrDefault();
            rept.Delete(r);

Let's Watch a Video!
A lot of people have asked for a "Rob special" video, and not to disappoint I've created one to show you how to get started (props to whomever knows the tune). It's 4 minutes and covers how to use SubSonic 3.0 (and its templates) with a Console App. You can also use this with any other app EXCEPT for a Web Site - for some reason T4 doesn't like those:

(Double-click for full screen, or you can download the WMV here).

 

The Download

You can download Preview 2 here. I've fixed/tweaked/changed a lot of stuff, including:

  • Fixed an issue with Columns not generating. I think this was because I wasn't closing connections properly, and some databases were case-sensitive. I've tested this on 2008 and 2005 and it works well.
  • Made every class Partial
  • Changed tables from Fields to Properties
  • Fixed a paging bug with the Linq translator
  • Renamed some TT files. Provider has become "QuerySurface" and Connection.TT is now called "_Utility.tt".
  • Renamed the no-code, required TT files to have underscore
  • Implemented MANY of the changes suggested in this post by jguerts (thanks!)

As always- if you have any questions please let me know. If they're long ones, please email me at microsoft (robcon) or my full name at Gmail.

Cheers!


Dave - April 5, 2009 - Great work!



Once thing, ive noticed, not sure if you already aware of...

SPs (and I guess other DB elements) with - in their name, cause invalid method names to be generated..
Mark Walker - February 26, 2009 - D'uh! I just "RTFM" (or README.txt) in this case: it says code is here http://code.google.com/p/subsonicthree/source/checkout.
Mark Walker - February 26, 2009 - Hi Rob,

This looks fantastic!



Are you any closer to releasing the source? I'm *really* keen to use this in our new project but reluctant to commit until it's either officially supported by someone or Open-Sourced



Thanks for your work on this!
OutOfTouch - February 24, 2009 - Where is the Subsonicproject.com site??????????????

It was up yesterday and gone today!!!!!!!!
drift - November 12, 2008 - nice, Thom Yorke -The Clock
robconery - November 12, 2008 - Close... no cigar :)

Edit: HA! You got it :). Yep - well done. Great song, awesome album. I listened to it the whole time I was coding this stuff...
Yogesh - November 12, 2008 - This is going fast!!
drift - November 12, 2008 - yeah I edited it after a quick check...now for a cigar
robconery - November 12, 2008 - BatchQuery takes whatever you throw at it :)
robconery - November 13, 2008 - Your web.config is pointed at the wrong DB...?
robconery - November 13, 2008 - It's linq...
Yogesh - November 12, 2008 - Great. Thanks for the quick reply.
robconery - November 13, 2008 - Google is your friend...
robconery - November 15, 2008 - Sweet! Thanks!
robconery - November 13, 2008 - Need more details please
Todd - November 12, 2008 - It just keeps getting better. Great work, Rob!
DougWilson - November 12, 2008 - Great stuff! You've got me pretty excited. How long until Beta? Weeks? Months?
PK - November 12, 2008 - Same Doug --- i' fully interested aswel. I'd love to see the StartKit updated to include SS 3.0.



What about IQueryNinja, if u don't like the word Surface? It does do secret stuff, keeping things out of site while winning.
drift - November 12, 2008 - yeah I'm listening to it now, is good to work to. Been working more to Mogwai and Explosions in the Sky lately and often fall back to Boards of Canada for unobtrusive music to code to.
Yogesh - November 12, 2008 - Rob, are there any plans to include a Batch Insert or Batch Update sort of thing?
Joseph Daigle - November 12, 2008 - This is very exciting. Keep up the pace. And I can't wait for the source to stablize so you can release it. Maybe dump your code into a public repository so we can see your periodic checkins?
josh - November 12, 2008 - its getting late so i'll finish reading this later.. in the meantime, big congrats to Rob and anyone who helped.
ajwaka - November 12, 2008 - I just thought "that sounds a Radiohead " when all of a sudden it was Thom Yorke's voice!!! NICE!!! I've gotta get "In Rainbows"



Keep the wort at a full boil Rob!
John Walker - November 12, 2008 - Rob,



This looks really great. I tried out one of your early, early releases and liked it back then.



We're currently porting a cash-cow VB6 Windows application to Winforms. This is a product that makes our company a good chunk o' change. To date the work we've done has been using Linq to SQL (which we really liked). However, we've gotten a good punch in the gut with the recent developments around Linq to SQL and Entity Framework. It's pretty clear Microsoft will be "investing" lots in Entity Framework and not so much in Linq to SQL.



So, on to the question. Our initial impression of the Entity Framework is that it's a bit too "heavy" for our needs. We like Linq to SQL (as said). SubSonic seems more in line with what we like...and it supports multiple RDBMS systems. Obviously, you can't make the decision for us, but can you lay out some of the use-cases you see for SubSonic? Would a "product" (that's being sold) like ours benefit from it as it's ORM underpinning? Any insight you have would be great.



Is SubSonic going to be open-source (maybe it is already). Anyway, very cool stuff. Thanks.
robconery - November 12, 2008 - Hi John - it's Open Source under New BSD and I'll be posting the source for

3.0 soon.

In terms of insight- I can tell you what my aim is - you can make up your

mind RE other stuff :).



No ORM is a silver bullet. I don't care much for ORMs for this reason :).

What I like is simple data access that I can modify to my needs. What I've

learned from SubSonic 2.x and below is that convention is nice and saves

time, but if you have a locked-in system people always seem to find the

edges; it's just the way it is.



I know I'm being vague - but I think you can appreciate why :).
John Walker - November 12, 2008 - Thanks Rob. I definitely appreciate why :). Gonna check it out and kick the tires. Thanks. Looks very nice.
Hrvoje - November 12, 2008 - Are joining and grouping implemented? In short, what I can't do now in this preview version, and can do with linq2sql? It would be nice to see some kind of comparison table, with future plans. Anyway, great job, I will probably switch to subsonic from linq2sql, and leave EF for big stuff.
Martin Nyborg - November 13, 2008 - I don't know how you are doing it but I have to say it again your are simple amazing



Now I have to go and exercise some code, can't wait
jchannon - November 13, 2008 - Rob,



Your projects are great and real help to us mear mortals. I have looked at both Subsonic and Subsonic MVC.



Will you be updating the MVC plugin with new your new code or would you say that are in sync in terms of functionality now.
Martin Nyborg - November 13, 2008 - I have just added a EntityBase class with some Helpers like INotifyPropertyChanged and a broken rules collection. It was very easy to make the changes.

So no the generated works well with databinding I guess :-)



The next step is implementing child collections I gees that I can figure out how to do that in the templates. But how do I fill them?

The I have another question - Can I make projections so I can create my own "Views" by joining tables?



And when some of the dust have settled, then it would be nice to have you explaining how the magic works.
Guy Harwood - November 13, 2008 - Nice video Rob, im sold!
josh - November 13, 2008 - ok now i've read the full post including video. this looks like very cool stuff. the demo had some really impressive points. I'm gonna have to read the code, cause looks like you guys poured all sorts of smarts into this version.
Subnus - November 13, 2008 - hey rob can you maybe update or create a new tutorial like http://subsonicproject.com/2-1-pakala/subsonic-writing-decoupled-testable-code-with-subsonic-2-1/ with then new subsonic 3.0 preview 2 or can't subsonic 3.0 not use own defined classes
robconery - November 13, 2008 - Yep - joins, groups, etc. Linq To Sql is a much more complete "system" of an ORM, meaning that it does Unit Of Work and tracks changes, handles concurrency, and so on.



SubSonic could do that if a UnitOfWork set of templates was made...
Erik_F - November 13, 2008 - Rob - having some trouble with the Classes.tt file - it's generating one class per table as expected, but the generated tables are generated without properties. What have I done wrong?



Meh. Never mind. *slaps forehead* Typo in the ProviderName setting.
mhnyborg - November 13, 2008 - I am still having 2 items on my list that I think is real show stoppers



- concurrency, maybe the database independent way with an int being incremented for each write to the database

- Transactions



Wish list

- alternate table, column names

- inheritance



robconery - November 13, 2008 - Concurrency you can enable yourself, however you want to.Transactions work

as they always have with SubSonic (I ported that over).



Alternate table/column names can be implemented by you in the templates.

Inheritance? Can you tell me more?
Subnus - November 13, 2008 - i try using this



public IQueryable GetAllPosts()

{

var posts = from p in db.GetQuery()

select p;



return posts;

}

but got a sql error that i does not know object post how can i make it work so know the table name is posts



the generated code is in namspace Subnus.MVC.Data.SubnusMVC if that helps
Kevin Deenanauth - November 13, 2008 - Wow I want this yesterday
mhnyborg - November 13, 2008 - >> Concurrency you can enable yourself

Can you give me a hint

>> Transactions work as they always have with SubSonic

I have never used SubSonic, can you give me code snippet with an update using transaction

>> Inheritance? Can you tell me more?

Yes of curse, I am working with an application that's mangeds antenna equipment and it would be nice to have a equipment base class that every equipment class implements and specific classes for each equipment type. Single table inheritance would be ok :-)
Alex Simkin - November 13, 2008 - The problem is that ProviderName in Settings.tt must be both: Connection string name (it is used in QuerySurface.tt) and database/catalog name (used in Classes.tt in GetSchema as catalog restriction). If name of the connection string differs from database name, nothing works.



2. Add ignores walues for primary keys, and if PK is not autogenerated, Add fails.
Subnus - November 13, 2008 - no subsonic 3 create the classes.cs file with all men tables in it i just want to use my own model just like you i the mvc storefront
wayde - November 13, 2008 - Have you ever thought about posting this on something like github so folks can simply fork the code/templates as they want to produce any number of possible ORM solutions in a more structured way?



Also, I'd be up for helping you modify the MVC addin to work with subsonic v.3. Personally, I see more value in going that approach to running the T4 templates than simply having them generate code for everything in your DB when you save one of the files. So following a Rails approach ... you define the model you want to create in your gui, hit run and it builds a migration file, your partial class, your iqueryable goodness and optional a controller and view.



Anyways, I'm rambling now so I'll end it here
Subnus - November 13, 2008 - found the problem a error 40 I have never used Linq before so got it wrong sould have been



public IQueryable GetAllPosts()

{

var posts = from p in db.Posts

select new Post

{

PostId = p.PostId,

Body = p.body,

CreatedBy = p.CreatedBy,

CreatedOn = p.CreatedOn,

Guid = p.Guid,

IsPublic = p.IsPublic,

IsDeleted = p.IsDeleted

};



return posts;

}



I thing I need to reed the book on my shelf "Linq in action" :)
Erik_F - November 13, 2008 - Right, this is exactly the problem I had. Primarily I don't want my generated repository name to be the same as the database name - once I set that up, however, everything worked.
Robert G - November 13, 2008 - Rob, awesome stuff as always. You've mentioned in some article or interview once that you see Subsonic used for rapid prototyping, but not for enterprise apps. Can you expand on that? Why not for enterprise apps?
Subnus - November 13, 2008 - can you give a example of joining and grouping with SubSonic can't get it to work



the sql I am trying to create is



select cat.CategoryId ,cat.Name

from SubnusMVC.Categories as cat

join SubnusMVC.Post_Category_Map as pcm on cat.CategoryId = pcm.CategoryId

where pcm.Postid =1
Subnus - November 13, 2008 - i have try with



var categories = from cat in db.Categories

select new Subnus.MVC.Data.SubnusMVC.Categories

{

Name = cat.Name,

CategoryId = cat.CategoryId

};



var postCategoryMap = from pcm in db.Post_Category_Map

select new Subnus.MVC.Data.SubnusMVC.Post_Category_Map

{

CategoryId = pcm.CategoryId,

PostId = pcm.PostId

};



var result = from category in categories

join postcat in postCategoryMap on category.CategoryId equals postcat.CategoryId

where postcat.PostId == postId

orderby category.Name

select new

{

CategoryId = category.CategoryId,

Name = category.Name

};

foreach (var item in result)

{

System.Diagnostics.Debug.Write(item.Name);

}



but i get a error the forach loop



do you have links to where i can learn linq
BlackMael - November 13, 2008 - Awesome stuff Rob!



My only issue (and a small one at that) is the templates generate C#. Are you planning on implementing VB versions of the templates?



In the meantime I have just converted the templates to generate VB.NET.



I had no major issues, though for some reason the extension methods for ToDictionary() and ToConstraintList() didn't want to show saying "T" didn't doesn't implement the methods. I've had issues with extensions methods before where they didn't seem to want to make themselves available to the objects they supposedly extend but I can't remember how I fixed it.



As a work around I just called the corresponding extension method via its static class. It seems to work or at least build for now. Next step is to build the rest of the application around it.

John Walker - November 13, 2008 - Ok, checking this out. When I drop the _Generated folder into the project, things seem to work fine, but at the end I get this warning regarding the _Settings.tt file:



Multiple template directives were found in the template. All but the first one will be ignored. Multiple parameters to the template directive should be specified within one template directive.



Any help would be appreciated.
John Walker - November 13, 2008 - Ok, figured this out....As mentioned above the ConnectionString name, Namespace, and ProviderName all have to be the same? Working now.
John Walker - November 13, 2008 - Ok, what am I doing wrong here?



static void Main(string[] args)

{

var db = new DB();



var query = from i in db.INCIDENTS

where i.RECEIVED_DT > DateTime.Now.AddYears(-1)

select i;



foreach (var item in query) { //bombs here on query object

Console.WriteLine(item.INCIDENT_TYPE);

}

}



This query fails with...



Unhandled Exception: System.NullReferenceException: Object reference not set to

an instance of an object.

at SubSonic.Linq.QueryBinder.VisitMemberAccess(MemberExpression m)

at SubSonic.Linq.ExpressionVisitor.Visit(Expression exp)

at SubSonic.Linq.QueryBinder.Visit(Expression exp)

at SubSonic.Linq.ExpressionVisitor.VisitMethodCall(MethodCallExpression m)

at SubSonic.Linq.QueryBinder.VisitMethodCall(MethodCallExpression m)

at SubSonic.Linq.ExpressionVisitor.Visit(Expression exp)

at SubSonic.Linq.QueryBinder.Visit(Expression exp)

at SubSonic.Linq.ExpressionVisitor.VisitBinary(BinaryExpression b)

at SubSonic.Linq.ExpressionVisitor.Visit(Expression exp)

at SubSonic.Linq.QueryBinder.Visit(Expression exp)

at SubSonic.Linq.QueryBinder.BindWhere(Type resultType, Expression source, La

mbdaExpression predicate)

at SubSonic.Linq.QueryBinder.VisitMethodCall(MethodCallExpression m)

at SubSonic.Linq.ExpressionVisitor.Visit(Expression exp)

at SubSonic.Linq.QueryBinder.Visit(Expression exp)

at SubSonic.Linq.QueryBinder.Bind()

at SubSonic.Linq.DbQueryProvider.Translate(Expression expression)

at SubSonic.Linq.DbQueryProvider.Execute(Expression expression)

at SubSonic.Linq.Query`1.GetEnumerator()

at SubSonicTest.Program.Main(String[] args) in C:\Temp\SubSonicTest\SubSonicT

est\Program.cs:line 19



If I change the query's where to a non-date related comparison, things work fine. This is my own database here. Just wondering if I'm doing something wrong or missing something.

Subnus - November 14, 2008 - i think there is a problem with subsonic



int albumid = 1;

var db = new Chinook.DB();

var query = from a in db.Album

where a.AlbumId == albumid

select a;



foreach (var item in query)

{

Console.WriteLine(item.Title);

}

Console.ReadLine();



does not work but



var db = new Chinook.DB();

var query = from a in db.Album

where a.AlbumId == 1

select a;



foreach (var item in query)

{

Console.WriteLine(item.Title);

}

Console.ReadLine();
mhnyborg - November 14, 2008 - This is working for me - Northwind database of curse :-)



[Test]

[Description("Selects products where categoryId==5")]

public void TestSimpleQueryQuery()

{

var db = new DB();

var result = from p in db.Products where (p.CategoryID == 5) select p;



foreach (var products in result)

Console.WriteLine(products.ProductName);



Assert.Greater(result.Count(), 1);

}



[Test]

[Description("Selects products where categoryId==5")]

public void TestSimpleQueryQueryWithIntParameter()

{

const int catId = 5;

var db = new DB();

var result = from p in db.Products where (p.CategoryID==catId) select p;



foreach (var products in result)

Console.WriteLine(products.ProductName);



Assert.Greater(result.Count(), 1);

}



[Test]

public void TestSimpleRepositoryQuery()

{

var repo = new NorthwindRepository();

var result = repo.Find(x => x.CategoryID == 5);



foreach (var products in result)

Console.WriteLine(products.ProductName);



Assert.Greater(result.Count(),1);

}



[Test]

public void TestSimpleRepositoryQueryWithIntParameter()

{

const int catId = 5;

var repo = new NorthwindRepository();

var result = repo.Find(x => x.CategoryID == catId);



foreach (var products in result)

Console.WriteLine(products.ProductName);



Assert.Greater(result.Count(), 1);

}

redsquare - November 14, 2008 - Rob,



Great work, many thanks for the preview. Cant wait for the Beta.



Red
Subnus - November 14, 2008 - okay my problem is that i passe albumid i a method and then i does not work



class Program

{

static void Main(string[] args)

{

var result = GetAlbumById(1);



foreach (var item in result)

{

Console.WriteLine(item.Title);

}

}



public static IQueryable GetAlbumById(int id)

{

var db = new DB();

var query = from a in db.Album

where a.AlbumId == id

select a;

return query;

}

} does not work i get at error in the foreach loop because it is at method i cant make the id a const



this is with the new tast database Chinook http://blog.wekeroad.com/blog/subsonic-3-0-repository-template-update/
Robert G - November 14, 2008 - That's odd, my ConnectionString name, Namespace, and ProviderName all have to be the same name, but I still get the warning.

Todd - November 14, 2008 - I ran a trace on SQL Server and noticed that the queries are not parameterized.



Are there plans to update the query engine to use parameterized queries so the server makes use of cached execution plans?
Jeff Doolittle - November 14, 2008 - Hey Rob,



T4 templates also do not work in a Smart Device project because the Compact Framework does not include the System.CodeDom namespace. But I found a way to make them work if you or your readers are interested: ( http://www.agilification.com/post/T4-Templates-For-The-NET-Compact-Framework.aspx ).



--Jeff
robconery - November 14, 2008 - Not parameterized? I'm using params with the commands-but SQL trace

wouldn't show u that. What do you mean?



Sent from my phone. Please excuse brief replies.
Todd - November 14, 2008 - Here's the output on the trace comparing Linq2Sql and Subsonic. I thought I remembered the Subsonic 2.0 traces looking more like the one for Linq2Sql.





Linq2Sql:

exec sp_executesql N'SELECT [t0].[ProjectId], [t0].[Name]

FROM [dbo].[Project] AS [t0]

WHERE [t0].[ProjectId] = @p0',N'@p0 int',@p0=1



Subsonic:

SELECT t0.Name, t0.ProjectId

FROM Project AS t0

WHERE (t0.ProjectId = 1)
cowgaR - November 15, 2008 - Hi Rob,

just watched the video (thanks! can't play with subsonic right now). I have 2 questions:



1. When you were showing System.Data.Common in action, e.g. changing the datastore from MS SQL to MySql database, you haven't changed values in settings.tt (you changed them only in app.config file).

ConnectionString and ProviderName values thus stayed unchanged, and it worked?



2. Why is the ProviderName value in settings.tt set to = "Northwind"? Shouldn't it be set to System.Data.SqlClient?



Now I am thinking if settings.tt file isn't redundant, or if it can't possibly read the settings from web/app.config file (is pure xml afterall).



thanks
robconery - November 15, 2008 - I was thinking after i replied yesterday that you were referring to this -

duh :). I'm wondering if this is the way DbCommands are created versus

SqlCommands. Since SqlCommands are platform-specific they can construct

parameterized queries - DbCommands, however, are generic so perhaps the

execution is different. This is interesting...
Todd - November 15, 2008 - For the above example I was running Preview 1, I forgot to mention that the trace also had two events for the statement: BatchStarting and BatchComplete.



This morning I tested with Preview 2. The repo.Find call and the Linq syntax I was using with Preview 1 now show up in the trace as a parameterized query...it must have just been Preview 1. I did notice though that the repo.GetAll call is showing up in the trace with the two events, BatchStarting and BatchComplete. The parameterized queries are showing up as RPC:Completed events.



I'm not sure if the Batch mode has something to do with it not being parameterized in Preview 1 but that's the only difference I see in the trace between Preview 1 and Preview 2.



repo.GetAll:

SELECT t0.Address, t0.City, t0.Company, t0.Country, t0.CustomerId, t0.Email, t0.Fax, t0.FirstName,

t0.LastName, t0.Phone, t0.PostalCode, t0.State

FROM Customer AS t0



repo.Find:

exec sp_executesql N'SELECT t0.Address, t0.City, t0.Company, t0.Country, t0.CustomerId, t0.Email, t0.Fax,

t0.FirstName, t0.LastName, t0.Phone, t0.PostalCode, t0.State

FROM Customer AS t0

WHERE (t0.CustomerId > @p0)',N'@p0 int',@p0=1



jf26028 - November 15, 2008 - There is an issue adding fields to the classes generated by Classes.tt. If you add any custom fields at all, the query will fail getting the objects from the database, stating that the newly added fields are invalid. It is as if, using reflection, you are getting all the fields of the object, and passing that to the select method, and then the query fails because the new fields do not belong in the select query.



For example, I have added List and List to one of the classes generated by Classes.tt via partial classes, and this is the error in the stack:



[SqlException (0x80131904): Invalid column name 'Permissions'.

Invalid column name 'Topics'.]

System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +1948826

System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +4844747

System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) +194

System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +2392

System.Data.SqlClient.SqlDataReader.ConsumeMetaData() +33

System.Data.SqlClient.SqlDataReader.get_MetaData() +83

System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +297

System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +954

System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162

System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32

System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141

System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12

System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10

SubSonic.DataProviders.DbDataProvider.ExecuteReader(QueryCommand qry) +228

SubSonic.Linq.DbQueryProvider.Execute(String commandText, String[] paramNames, Object[] paramValues, Func`2 fnRead) +233

SubSonic.Linq.DbQueryProvider.Execute(Expression expression) +1850

SubSonic.Linq.Query`1.GetEnumerator() +69

System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) +7663302

System.Linq.Enumerable.ToList(IEnumerable`1 source) +61

test.DataServices.SubSonicDataService.ListCategories()



Hope that makes sense,



Jesse
Germán - November 16, 2008 - Hi Rob,



Using your preview, I'm doing a simple query and I'm getting an exception, something about being unable to map the parameter in the where clause (for which the compiler generates a special type MediaService+<>c__DisplayClass0).



public MediaAsset GetMediaAsset(int id)

{

return_mediaRepository.GetMediaAsset()

.Where(x => x.IDMediaAsset == id)

.SingleOrDefault();

}





Saludos!!!





I'm getting this.

ArgumentException: No hay ninguna asignación de tipo de objeto Sherezade.Services.MediaService+<>c__DisplayClass0 a un tipo nativo de un proveedor administrado conocido.]

System.Data.SqlClient.MetaType.GetMetaTypeFromValue(Type dataType, Object value, Boolean inferLen) +1917459

System.Data.SqlClient.SqlParameter.GetMetaTypeOnly() +4870773

System.Data.SqlClient.SqlParameter.Validate(Int32 index, Boolean isCommandProc) +17

System.Data.SqlClient.SqlCommand.BuildParamList(TdsParser parser, SqlParameterCollection parameters) +203

System.Data.SqlClient.SqlCommand.BuildExecuteSql(CommandBehavior behavior, String commandText, SqlParameterCollection parameters, _SqlRPC& rpc) +237

System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +954

System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162

System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32

System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141

System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12

System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10

SubSonic.DataProviders.DbDataProvider.ExecuteReader(QueryCommand qry) +228

SubSonic.Linq.DbQueryProvider.Execute(String commandText, String[] paramNames, Object[] paramValues, Func`2 fnRead) +233

SubSonic.Linq.DbQueryProvider.Execute(Expression expression) +1850

SubSonic.Linq.QueryProvider.System.Linq.IQueryProvider.Execute(Expression expression) +72

System.Linq.Queryable.SingleOrDefault(IQueryable`1 source) +269

Sherezade.Services.MediaService.GetMediaAsset(Int32 id) in D:\Proyectos\Web\Sherezade\Sherezade.Services\Media\MediaService.cs:42

Sherezade.Web.Controllers.MediaController.Get(Int32 id, Nullable`1 width, Nullable`1 height) in D:\Proyectos\Web\Sherezade\Sherezade.Web\App\Controllers\MediaController.cs:96

lambda_method(ExecutionScope , ControllerBase , Object[] ) +224

System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +51

System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(MethodInfo methodInfo, IDictionary`2 parameters) +568

System.Web.Mvc.<>c__DisplayClassc.b__9() +117

System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +305

System.Web.Mvc.<>c__DisplayClasse.b__b() +61

System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +305

System.Web.Mvc.<>c__DisplayClasse.b__b() +61

System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(MethodInfo methodInfo, IDictionary`2 parameters, IList`1 filters) +455

System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +736

System.Web.Mvc.Controller.ExecuteCore() +180

System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +96

System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +36

System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +377

System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +71

System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +36

System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181

System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75







robconery - November 16, 2008 - Yah turns out variables aren't getting cast right. I'll fix for next

drop
dlambert - November 18, 2008 - Very good stuff. I ran into an issue with the generated classes if you've got a field in a table that's named the same as the table itself (I know, this is ridiculous, but that's how the DB is built). Ex of generated code:



public partial class file_type {

public int file_type_id { get; set; }

public string file_type { get; set; } // error here: "Member names cannot be the same as their enclosing type

}



Otherwise, very cool stuff. Hat's off!

jf26028 - November 20, 2008 - Has anyone successfully added properties to the model classes via partial classes? All queries fail after I add custom properties for me.
bbqchickenrobot - November 21, 2008 - hahahah! I 2nd IQueryNinja lol!
Gbenga - November 23, 2008 - Where can i download the (Starter Site comes pre-wired with SubSonic, Membership, AJAX, Useful Utilities, and the FCK Editor).
mhnyborg - November 24, 2008 - I am waiting and waiting, and I am telling myself that I have to be patience. but I just need a new fix. Please don't let me wait until Christmas for episode 3.



And do you have some kind of road map for this very cool spike?
robconery - November 24, 2008 - Awesome! Thanks!
bbqchickenrobot - November 24, 2008 - This is cool. Way cool. It's so cool it should be classified top secret.
bbqchickenrobot - November 24, 2008 - Modifying the templates to work against our test database... ran across some naming issues, such as c# keyword conflicts with our sproc parameter names. Also, some of our old dba's liked to use '$' characters in the names of tables as well as the '-' char. Believe it or not, the "\" was a problem too. So, I created some new methods in the _Utility.tt class and altered the existing CleanUp() method. It's just a quick and dirty version, but figured it may save others a minute or two.....



string CleanUp(string tableName){

string result=tableName;



//strip blanks etc...

result=result.Replace(" ","_")

.Replace("$","")

.Replace("-","_")

.Replace("/","")

.Replace(@"\","")

.Replace("'","");



if(NumberExtensions.IsNumber(tableName.Substring(0, 1)))

{

result="_"+result;

}



return result;

}





string PrefixName(string objectName)

{

return "_" + objectName;

}

string KeywordCleanUp(string objectName)

{



switch (objectName)

{

case "abstract":

return PrefixName(objectName);

break;

case "event":

return PrefixName(objectName);

break;

case "new":

return PrefixName(objectName);

break;

case "struct":

return PrefixName(objectName);

break;

case "as":

return PrefixName(objectName);

break;

case "explicit":

return PrefixName(objectName);

break;

case "null":

return PrefixName(objectName);

break;

case "switch":

return PrefixName(objectName);

break;

case "base":

return PrefixName(objectName);

break;

case "extern":

return PrefixName(objectName);

break;

case "object":

return PrefixName(objectName);

break;

case "this":

return PrefixName(objectName);

break;

case "bool":

return PrefixName(objectName);

break;

case "false":

return PrefixName(objectName);

break;

case "operator":

return PrefixName(objectName);

break;

case "throw":

return PrefixName(objectName);

break;

case "break":

return PrefixName(objectName);

break;

case "finally":

return PrefixName(objectName);

break;



case "out":

return PrefixName(objectName);

break;



case "true":

return PrefixName(objectName);

break;

case "byte":

return PrefixName(objectName);

break;

case "fixed":

return PrefixName(objectName);

break;

case "override":

return PrefixName(objectName);

break;

case "try":

return PrefixName(objectName);

break;

case "case":

return PrefixName(objectName);

break;

case "float":

return PrefixName(objectName);

break;

case "params":

return PrefixName(objectName);

break;

case "typeof":

return PrefixName(objectName);

break;

case "catch":

return PrefixName(objectName);

break;

case "for":

return PrefixName(objectName);

break;

case "private":

return PrefixName(objectName);

break;

case "uint":

return PrefixName(objectName);

break;

case "char":

return PrefixName(objectName);

break;

case "foreach":

return PrefixName(objectName);

break;

case "protected":

return PrefixName(objectName);

break;



case "ulong":

return PrefixName(objectName);

break;

case "checked":

return PrefixName(objectName);

break;

case "goto":

return PrefixName(objectName);

break;

case "public":

return PrefixName(objectName);

break;

case "unchecked":

return PrefixName(objectName);

break;

case "class":

return PrefixName(objectName);

break;

case "if":

return PrefixName(objectName);

break;

case "readonly":

return PrefixName(objectName);

break;

case "unsafe":

return PrefixName(objectName);

break;

case "const":

return PrefixName(objectName);

break;

case "implicit":

return PrefixName(objectName);

break;

case "ref":

return PrefixName(objectName);

break;

case "case ":

return PrefixName(objectName);

break;

case "ushort":

return PrefixName(objectName);

break;

case "continue":

return PrefixName(objectName);

break;

case "in":

return PrefixName(objectName);

break;

case "return":

return PrefixName(objectName);

break;

case "using":

return PrefixName(objectName);

break;

case "decimal":

return PrefixName(objectName);

break;

case "int":

return PrefixName(objectName);

break;

case "sbyte":

return PrefixName(objectName);

break;

case "virtual":

return PrefixName(objectName);

break;

case "default":

return PrefixName(objectName);

break;

case "interface":

return PrefixName(objectName);

break;

case "sealed":

return PrefixName(objectName);

break;

case "volatile":

return PrefixName(objectName);

break;

case "delegate":

return PrefixName(objectName);

break;

case "internal":

return PrefixName(objectName);

break;

case "short":

return PrefixName(objectName);

break;

case "void":

return PrefixName(objectName);

break;

case "do":

return PrefixName(objectName);

break;

case "is":

return PrefixName(objectName);

break;

case "sizeof":

return PrefixName(objectName);

break;

case "while":

return PrefixName(objectName);

break;

case "double":

return PrefixName(objectName);

break;

case "lock":

return PrefixName(objectName);

break;



case "stackalloc":

return PrefixName(objectName);

break;

case "else":

return PrefixName(objectName);

break;

case "long":

return PrefixName(objectName);

break;

case "static":

return PrefixName(objectName);

break;

case "enum":

return PrefixName(objectName);

break;

case "namespace":

return PrefixName(objectName);

break;

case "string":

return PrefixName(objectName);

break;

case "from":

return PrefixName(objectName);

break;

case "get":

return PrefixName(objectName);

break;

case "group":

return PrefixName(objectName);

break;

case "into":

return PrefixName(objectName);

break;

case "join":

return PrefixName(objectName);

break;

case "let":

return PrefixName(objectName);

break;

case "orderby":

return PrefixName(objectName);

break;

case "partial":

return PrefixName(objectName);

break;

case "select":

return PrefixName(objectName);

break;

case "set":

return PrefixName(objectName);

break;

case "value":

return PrefixName(objectName);

break;

case "var":

return PrefixName(objectName);

break;

case "where":

return PrefixName(objectName);

break;

case "yield":

return PrefixName(objectName);

break;

default:

return objectName;

}

}



Dmitriy Nagirnyak - November 25, 2008 - Rob and all the guys here,



Wouldn't you mind to update the comparison or O/R-M tools here:

http://dnagir.blogspot.com/2008/11/orm-requirements-analysis.html



Maybe some info on Subsonic.



Thanks.
Benjamin - November 25, 2008 - All I can say is "Rob, you rock!"
bbqchickenrobot - November 25, 2008 - When you insert a record, the primary key field isn't auto-populated on the POCOs when utilizing GUID or AutoNumber Integers. Is this going to make it in the final version - or are you going to leave it up to us to add support for this?
robconery - November 26, 2008 - I'll make sure it gets in there...
Emmanuel - November 26, 2008 - Hello Rob,



Loving this preview, I already started playing with it. Got a couple of questions though. Forgive me if my questions seem stupid.



1. Will the linq stuff support linq queries such as 'from p in db.Products where p.Category == "blue" '?

2. I want my generated to have inheritance and relationships, so i'll be customizing the templates to accomplish that, I think I got an idea on how to do this, but I wanted to know if the Linq provider can handle this. For example, let's say if I have a class Customer that has a collection of Products as one of its properties which represent the relationship. Will the Linq provider be able to generate the query with the right joins? Secondly, on insert will it be able to generate the inserts for the two different tables (customers and products) with the proper keys?



3. What's the title of the song you're playing on that video? I've watched the video a couple i think that song is growing on me :).



Thanks



Tom - November 26, 2008 - Rob



Quick question, is this supposed to work w/ Oracle (or any other db) yet? I see in the video you just added a reference to MySQL client library, changed your connection string and got it running right away. I tried swapping out the conn string to my Oracle DB and got no such love. Looking into _Utility.tt, it looks like you've got SqlConnection, SqlCommand, etc hard coded in there instead of DbConnection, DbCommand, etc. So my guess is that the download only works against SqlServer and the video is previewing the ability to swap dbs on the fly???



Great work as always. I really like where this is going.

robconery - November 26, 2008 - Two things on this -

1) I changed the connection string, but I also changed the provider. I'm

pretty sure Oracle supports the Data Factory - but no I haven't tested it

yet.



2) In your code you can use whichever provider you want since all it needs

is the schema - I'll try and change this (it's in the queue) going forward.
Tom - November 26, 2008 - Thanks for the response.



1) I did change the provider as well (by "connection string" I meant connection string entry in the configuration file - always say what you mean, right?). Anyway, here's what I have:



In App.config:







In _Settings.tt:



const string ConnectionString=@"Data Source=bar;Persist Security Info=True;User ID=foo;Password=foobar;";

const string Namespace = "Foo";

const string ProviderName="Foo";

string[] ExcludeTables= new string[]{"sysdiagrams","SubSonicSchemaInfo"};



When I build I get a transformation error on classes.tt:



Running transformation: System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)

at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)

at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)

at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, SqlConnection owningObject)

at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, Int64 timerExpire, SqlConnection owningObject)

at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(String host, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, Int64 timerStart)

at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject, SqlConnectionString connectionOptions, String newPassword, Boolean redirectedUserInstance)

at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, String newPassword, SqlConnection owningObject, Boolean redirectedUserInstance)

at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)

at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options)

at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject)

at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject)

at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)

at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)

at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)

at System.Data.SqlClient.SqlConnection.Open()

at Microsoft.VisualStudio.TextTemplating116F6562DC44BE6ABBCF1D25C0C5BF9E.GeneratedTextTransformation.GetConnection() in c:\Documents and Settings\twayso\My Documents\Visual Studio 2008\Projects\subsonic_3_p2_test\_Generated\_Utility.tt:line 19

at Microsoft.VisualStudio.TextTemplating116F6562DC44BE6ABBCF1D25C0C5BF9E.GeneratedTextTransformation.GetSchema(String meta, String[] restrictions) in c:\Documents and Settings\twayso\My Documents\Visual Studio 2008\Projects\subsonic_3_p2_test\_Generated\_Utility.tt:line 47

at Microsoft.VisualStudio.TextTemplating116F6562DC44BE6ABBCF1D25C0C5BF9E.GeneratedTextTransformation.GetSchema(String meta) in c:\Documents and Settings\twayso\My Documents\Visual Studio 2008\Projects\subsonic_3_p2_test\_Generated\_Utility.tt:line 43

at Microsoft.VisualStudio.TextTemplating116F6562DC44BE6ABBCF1D25C0C5BF9E.GeneratedTextTransformation.GetTables() in c:\Documents and Settings\twayso\My Documents\Visual Studio 2008\Projects\subsonic_3_p2_test\_Generated\_Utility.tt:line 40

at Microsoft.VisualStudio.TextTemplating116F6562DC44BE6ABBCF1D25C0C5BF9E.GeneratedTextTransformation.TransformText() in c:\Documents and Settings\twayso\My Documents\Visual Studio 2008\Projects\subsonic_3_p2_test\_Generated\Classes.tt:line 8 C:\Documents and Settings\twayso\My Documents\Visual Studio 2008\Projects\subsonic_3_p2_test\_Generated\Classes.tt 1 1



2) I did start trying modifying the the _Utility.tt code to use the OracleClient to get the schema info, but I was getting error after error so I gave up. After seeing your reply though, I think I'll try changing it to use the Data.Common objects and get it to work. I'll let you know what I come up with.



Happy T-day!
MortMan - November 27, 2008 - SubSonic 3.0 looks awesome! I took it for a spin on a tiny test database to keep the schema to a minimum. Then I played around with SPs for a bit. Now as far as I can tell the generate code allows me to make calls like db.AllPersons().ExecuteTypedList(). That works just fine. But what if I have to add parameters? Should I then implement extra methods that wrap the Command.Parameters.Add() calls in a partial DB class?



Also it seems that the StoredProcedure.ExecuteScalar() always results in an error such as "Incorrect syntax near 'PersonNameByID'." (my SP is called PersonNameByID.)

MortMan - November 27, 2008 - Well the first bit is my own fault for not reading the comments. Seems ProviderName in _Settings.tt is assumed to be the same as the Database name (mine wasn't). Now everything works somewhat better ;)
MortMan79 - November 27, 2008 - - empty -
MortMan79 - November 27, 2008 - In Robs videos he isn't actually running the .tt scripts against MySQL he is only using the same generated code against MySQL. Anyhow I want to get SubSonic working with Oracle so I added a data provider in the _settings.tt file:



const string DataProvider = "System.Data.OracleClient";



Then I modified the _Utility.tt to use the DataFactory class based on the DataProvider as shown below. This works but gives trouble with the schema retrieval. I will have to look at it again. Also the remaining code, mapping datatypes to from C# to SQL and retrieval of foreign keys, is hardcoded to MS SQL so that will need to be tweaked. Here is my modified _Utility.tt :





<#@ include file="_Settings.tt" #>

<#+



string CleanUp(string tableName){

string result=tableName;



//strip blanks

result=result.Replace(" ","_");



//put your logic here...



return result;

}



DbProviderFactory GetFactory(){

return DbProviderFactories.GetFactory(DataProvider);

}



DbConnection GetConnection(){

string connectionString=ConnectionString;



DbConnection conn= GetFactory().CreateConnection();

conn.ConnectionString = connectionString;

conn.Open();

return conn;

}

string GetPK(string table){

string[] restrictions = new string[4] { ProviderName, null, table, null };

DataTable tbl=GetSchema("IndexColumns",restrictions);;

string pk="";

foreach(DataRow dr in tbl.Rows){

string index=dr["constraint_name"].ToString();

if(index.StartsWith("PK_")){

pk=dr["COLUMN_NAME"].ToString();

break;

}

}

return pk;

}

DataTable GetColumns(string table){

string[] restrictions = new string[4] { ProviderName, null, table, null };

return GetSchema("COLUMNS",restrictions);

}

DataTable GetTables(){

return GetSchema("tables");

}

DataTable GetSchema(string meta){

return GetSchema(meta, new string[4]);

}

DataTable GetSchema(string meta, string[] restrictions){

DataTable tbl=null;

using(DbConnection conn=GetConnection()){

tbl= conn.GetSchema(meta, restrictions);

}

return tbl;

}

DataTable GetSPs(){

return GetSchema("Procedures");

}

DataTable GetFKs(string tableName){

DataTable tbl=new DataTable();

using(DbConnection conn = GetConnection()){

DbCommand cmd= GetFactory().CreateCommand();

cmd.CommandText = FKSql;

cmd.Connection = conn;



DbParameter parm = GetFactory().CreateParameter();

parm.ParameterName = "@tableName";

parm.Value = tableName;

cmd.Parameters.Add(parm);



DbDataAdapter da = GetFactory().CreateDataAdapter();

da.SelectCommand = cmd;



da.Fill(tbl);

}

return tbl;

}

DataTable GetSPParams(string spName){

string[] restrictions = new string[4] { ProviderName, null, spName, null };

return GetSchema("ProcedureParameters", restrictions);

}



------- SNIP the remaining code ----
HS - November 27, 2008 - I have done the same thing. But after i solved the problems with the schema discovery (check the ODP.NET documentation for details) i am stuck since Subsonic is generating invalid sql for Oracle, for example:

SELECT t0.field1, t0.field2 FROM tablename AS t0



In Oracle this is not valid because in that sql the "AS" is considered the table alias.

Is there some way we can override the sql statement generation or will we have to wait for the next preview?



Anyway, great work Rob.
robconery - November 27, 2008 - Delete



Sent from my phone. Please excuse brief replies.
Bret - December 2, 2008 - Wow Subsonic keeps growing and it seems that it is keeping up with the new development of .NET. I see that you have tested your code on SQL server 2005 and 2008. The one thing that at times seems lacking is the love to some of those "other" Databases. The largest advantage to Subsonic is the fact that I can learn one API/tool and then work with SQLServer/MySQL/ORACLE/SQLite and whatever else I may need to work with without spending all of my time worrying about the idiosynchrosies of the specific database. As you are testing maybe you could "eat your own dogfood" as the saying goes and try to do all of your testing in say Oracle for a month (I know I wouldn't want to do that). I built a DAL off of Subsonic a while back and I loved the fact that when I had to target a different database I could basically use the Dal I had created for Oracle and just change the connection string and a couple parameters in the Web.config and then run the same web site with a SQL Server back end.



It just seems that the majority of all bugs that I run into with Subsonic are bugs that only happen in Databases other than SQL Server.
Ibleif - December 3, 2008 - Is it soon time for preview 3? Can't wait to see more of this stuff.
Dan - December 4, 2008 - I have just been trying out SubSonic Preview release 2, and can get the basic demos shown in the video working. However, whenever I use a query where the criteria parameter is a variable (using both DB... and the Repository) I get the following exception:

"No mapping exists from object type ... to a known managed provider native type."



e.g. this works fine:



var query = repo.Find(x => x.ProductID == 1).SingleOrDefault();



this throws the execption:



int id = 1;

var query = repo.Find(x => x.ProductID == id).SingleOrDefault();



As said, it also throws the exception using the DB class e.g.



string firstName = "Nancy";

var db = new DB();

var result = (from e in db.Employees where e.EmployeeID == criteria.Value select e).SingleOrDefault();



I'm using VS 2008 Professional, SQL Server 2005 Express with the Northwind database from CodePlex. It is a C# console app with nothing other than the SubSonic assembly referece. Any ideas?



Dan.
Dan - December 4, 2008 - The second example is incorrect (too quick with the old cut and paste) and should read as follows:



string firstName = "Nancy";

var db = new DB();

var result = (from e in db.Employees where e.FirstName == firstName select e).SingleOrDefault();
robconery - December 4, 2008 - Yep - know about this one. It's fixed in the next drop...
minus4 - December 12, 2008 - i dont get anywhere with this, followed the video file and simply changed my connection to be mysql connection

and i doesent work, infact its plain wierd:



i get this error:



the type of namespace DB could not be found (repository,cs) LIne 26 column 23



but what the strange thing is is that





namespace Northwind {



///

/// A Repository class which wraps the Northwind Database

///


public class NorthwindRepository:IRepository where T:new() {



IQuerySurface _db;

public NorthwindRepository(IQuerySurface db) {

_db = db;

}

public NorthwindRepository() {

_db = new DB();

}



is the filem, but im not using northwind, this is all rubbish if it will only ever work with a namespace called northwind.

i have setup my app.config and setting.tt to both say mysqltest as the namespace........... so anyone please...... i was perfectly happy with subsonic 2 except it never did stored procedures, now i am worse off cos not only do i not have stored procedures still, but i dont even have the DB code anymore.



help much appreciated
Kiri - January 12, 2009 - Nice Intro article.. Thank U....!!!

Cheers
Pickled - April 17, 2009 - Hi BlackMael, How hard was it to convert over the templates to generate vb.net? I've only just started using SubSonic v2.2 and impressed so far but put off going to v3 because of missing templates for VB. Of course, if you're happy to share :-) Appreciate your help. Tx
ApasySarCap - April 17, 2009 - nice, really nice!
Jorge - April 30, 2009 - I am migrating from subsonic to hibernate, because I think this project was died! Is there any site to see the new features and releases?
Fix My Script - June 5, 2009 - Fix My Script offers practical, low cost script installation and website building solutions for people with high standards. Our proven expertise and total commitment to quality enables us to provide the most comprehensive service in the industry. Fix My Script programmers can install, debug and test PHP scripts, fix script problems and errors.
James White - June 19, 2009 - If you do any table renaming, line 61 in QuerySurface.tt needs to change from string tableName=dr[2].ToString().Replace(" ","_"); to string tableName=CleanUp(dr[2].ToString());
Steve - September 11, 2009 - I think creating a 'full blown unit of work' is a big deal. Huge. And quite honestly a 'ORM' without a unit of work is really not an ORM as far as I'm concerned.
Rob Conery - September 11, 2009 - I can understand that :) and while I think your definition of ORM is just a bit restricting - the good news is there are plenty out there that fit it :). I'm not building it into SubSonic - it's noise :)
Mark - October 1, 2009 - Rob,
Our state's agencies prefer to use Oracle. Would you let me know who is working on the TT templates for Subsonic with the Oracle provider?

Great work on version 3. I hope you're getting paid somehow to maintain it! :)
Dadang - October 5, 2009 - thanks for the video.... waiting for the next video for installing 3.0.3.... and also pls put the ebook subsonic here...
So many thankss....
Gecko