Table Wrapper vs. Database Wrapper

A few commenters asked (like Kevin Pang) why this isn't a database wrapper instead of a table wrapper. The short answer is that it's a bit more concise to query against tables and primary keys - building commands against settings rather then forcing the developer to use too many strings to dictate which table name to use.

Strippage

I liked the idea of the Callbacks - but I ripped those out because validation should be handled elsewhere - preferably in your app or on your model using IValidatable. Up to you. Either way I took those out since they were a "nice to have" - not core to the Fx.

UpdateMany and InsertMany were redundant, and moreover not terribly useful. What's more useful is to execute a series of objects as BOTH Inserts and Updates within a transaction. I added that (which I'll talk about below.)

Fixes

One of the commenters pointed out that "SCOPE_IDENTITY" doesn't work in the same execution call with SQL CE. So I made a change to that so "SELECT @@IDENTITY" is used in the same connection. This isn't ideal - if you're using SQL Server you might want to change that back in the CreateInsertCommand() method.

I asked

Rob Sullivan (my favorite go-to DBA to pick on) to blow this thing up and tell me why he hates it. He sort of did that, but then got excited because he was able to drop below the abstraction quite easily and pimp the thing out to his liking. In short - he recommended that I add a Size constraint to the DbParameter when a string is used - this makes sure that query plans are honored. Yay Open Source!

Additions

You can now get nice and cuddly with the core DBCommands if you like, or ignore them entirely. I've added the ability to execute various commands (Insert, Update, and Delete) from any table within the scope of a transaction.

Connection Strings

You no longer have to specify the connectionStringName when you inherit DynamicModel. If you don't specify it, Massive will pull the first connectionString it finds in your web/app.config.

Broader Transaction Abilities

You can now transact objects, as if they were SQL statements. Let's say you want to add 2 new Categories:

var cats = new Categories(); var cat1 = new { CategoryName = "Piggy Bacon", Description = "Delete me" }; var cat2 = new { CategoryName = "Festivus Punch", Description = "Delete me" }; //add them both in the scope of a single connection/transaction cats.Transact(cat1, cat2);

"cat1" and "cat2" are considered "inserts" because the PrimaryKeyField isn't present. If it was, they would be considered "Updates".

You can also get funky and add/delete a bunch of stuff at once:

var cats = new Categories(); var products = new Products();

var cat1 = new { CategoryName = "Piggy Bacon", Description = "Da best kine" }; var cat2 = new { CategoryName = "Festivus Punch", Description = "Holiday fun" };

var p1 = new {ProductName = "Beef Punch", ProductID = 12} var p2 = new {ProductName = "Dim Sum Yum Yum", ProductID = 24}

//make all these changes in a single tranny var commandList = new List(); commandList.AddRange(products.BuildCommands(p1,p2)); commandList.AddRange(cats.BuildCommands(cat1,cat2))

//while we're at it let's remove Categories that should be deleted commandList.Add(cats.CreateDeleteCommand(" where description LIKE @0", "Delete me");)

//execute them together - the "table" we choose here doesn't matter products.Execute(list);

Column Lists

A few people asked about columns - getting away from "SELECT *". The reason why you might want to do this is evident with the Northwind Categories table - it has pictures. Sometimes all you want is a name:

var cats = new Categories(); var names = cats.All(columns:"CategoryName");

A few people have asked how you can "map column names" - you would push that down to SQL if you needed to:

var cats = new Categories(); var names = cats.All(columns:"CategoryName as Bubbles"); var first = names[0].Bubbles;

This brings up a new thing in C# 4.0 - and one the reasons I'm able to squeeze all of this intoIt would be easy to get locked into a horrible mess with the "All()" method here - WHERE statements, limits, column names, etc. However I'm able to keep it to a single method - defaulting all of the arguments to a logical value.

This allows you to write code that is a bit more clear - and I kind of like it.

Pushing and Pulling

I'll push this to Github in about 10 minutes (I want to check on one or two things first). I love the feedback that's been coming in and hope it keeps coming - when that "planes out" I'll push a new update to NuGet.

Speaking of NuGet - there's an issue that I've raised to the team where Massive.cs is being dropped into AppCode as "Content" - which means it doesn't get compiled by default. This is a bit maddening - but I built the thing to work primarily with WebMatrix so I don't want to change the AppCode stuff.

If you use it in MVC/WebForms/Whatever - make sure you set the Properties/BuildAction to "Compile".

Oh yah - managed to refactor everything here, added some comments, and LOST 15 lines of code!385 Lines