Hanalei, Hawaii 9/2/2010
438 Posts and Counting

SubSonic 3.0: The SimpleRepository

Wednesday, June 10, 2009 -

I have a little downtime right now (which I’ll write about in a later post – and yes I still have my job) so I’m using it to put the wraps on SubSonic 3.0 (soon, soon). One feature I’m very, very happy about is the new SimpleRepository, which allows you to use POCOs the way you want, using Migrations the SubSonic way: magically.

It’s Rev 1, Be Gentle! I’ve put peddle to metal with this feature, using it as the backbone of my entire test suite – I think it’s pretty close to being “good to go”. That said – this is Open Source, if you find a bug I’d love your help with a patch. This is not Big Software and we won’t think of every scenario – but hopefully most of them.

Elevator Summary
The SimpleRepository is for people who don’t want a bunch of “fluff and object noise” in their application and who want their objects “to just be persisted somewhere” without having to design a DB to create an object model. It’s all about POCOs first – sort of an Object Database feel where your object is “just saved”.

This Repository is bare-knuckles, get it going quick, ZERO CONFIGURATION “just gimme my damn data and get out of my way and no don’t ask me which DB I’m using” kinda deal. I should have called it “BareKnuckleRepository”… hmmm.

It works with SQL 2005, 2008, MySQL, and SQLite. Oracle drives me crazy and if someone’s inclined the fix to put it in takes all of 30 minutes. Same with PostGres.

How It Works
The first thing is to create a simple object – I’ll use the ubiquitous Product here:

    public class Product {
        public int ProductID { get; set; }
        public Guid Sku { get; set; }
        public int CategoryID { get; set; }
        public string ProductName { get; set; }
        public decimal UnitPrice { get; set; }
        public bool Discontinued { get; set; }
    }

 

Next, you need to tell SubSonic about your database. You do this by using our ProviderFactory and passing in a connection string, or not:

var provider=ProviderFactory.GetProvider("SubSonicTest");

 

UPDATE: I just checked in a change so you don’t need to declare the Provider – it will do it for you. You can just pass in the connection string name, or nothing at all and we’ll try and guess what it is.

You can pass in a connection string name, the full connection string, or nothing at all and we’ll do our best to pull it from your config. This is basically a glorified wrapper to your database.

Then you create a SimpleRepository, passing in the provider you just created:

var repo=new SimpleRepository(provider,true);

The boolean argument there is the magic button – I’ll talk more about this in a second. This repository is very, very simple and has things like “Single, Find, Add, Update, Delete” etc. and can save bunches of items in a transaction.

For this example let’s use an empty DB I just created (the connection string above points to it):

sstest

Next, work up your Product object:

var newProduct=new Product();
newProduct.Sku=Guid.NewGuid();
newProduct.CategoryID=5;
newProduct.ProductName="Pretzel";
newProduct.UnitPrice=100;
newProduct.Discontinued=false;

And then save it using the Repo:

repo.Add<Product>(newProduct);

This is where the magic happens :). By setting “true” in the SimpleRepository constructor, you’re telling it to run Migrations for you – automatically – creating your table if it’s not there.

If you refresh your DB…

sstest2

Yes – if you change your model then your DB will be synchronized. No, you will not lose data (unless you’re using SQLite , which doesn’t support ALTER of anything). You can change names, types, lengths, add/remove columns etc – all without dropping the table.

Notice that the Primary Key was created for you, and the columns defaulted to nice lengths etc. It also made sure the data was added…

sstest3

And yes, the Add() method returns the newly inserted key – for all providers.

SimpleRepo Convention
It’s not easy to make decisions as to how to store certain columns – and I had to do something I normally don’t like, and that is create a set of very, very small Attributes to help out.

  • Primary Keys: If you call a column “ID” or “Key” or “[ClassName]ID” – no matter it’s type – that will be your Primary Key. If you have other things in mind you can use a primary key attribute (“SubSonicPrimaryKey”) and we’ll use that column.
  • String length: there are two ways to tell SubSonic how to handle this – both using attributes. The first is “SubSonicStringLength(int length)” and the second is “SubSonicLongString” which sets to nvarchar(MAX) or LONGTEXT – depending on your provider.
  • Nullability: The default is not null, but you can change this by making your propery a nullable type.
  • Numeric Precision: the default is a Precision of 10 and a scale of 2 but you can change that with the “SubSonicNumericPrecision(int precision, int scale)” attribute.
  • Ignoring a property: you can ignore generation of a property by using “SubSonicIgnore” attribute.

This is my test class:

    public class SubSonicTest {

        public Guid Key { get; set; }
        public int Thinger { get; set; }
        public string Name { get; set; }
        [SubSonic.SubSonicStringLength(500)]
        public string UserName { get; set; }
        public DateTime CreatedOn { get; set; }
        public decimal Price { get; set; }
        public double Discount { get; set; }
        
        [SubSonic.SubSonicNumericPrecision(10,3)]
        public float? Lat { get; set; }
        [SubSonic.SubSonicNumericPrecision(10, 3)]
        public float? Long { get; set; }

        public bool SomeFlag { get; set; }
        public bool? SomeNullableFlag { get; set; }
        [SubSonic.SubSonicLongString]
        public string LongText { get; set; }
        [SubSonic.SubSonicStringLength(800)]
        public string MediumText { get; set; }
        [SubSonic.SubSonicIgnore]
        public int IgnoreMe { get; set; }
    }

And if you have a look here at our source testing, you can see how this class is used in my unit tests (not an easy thing to do!).

You can also have a look here to see how the SimpleRepository works under test.

Summary Summary
I’m working on perf tweaks right now, and still adding things to the docs so it will be a bit yet until I release this. If you want to have at our source – it’s here.

I’m not going to release 3.0 until I feel that people can get up and going – and there is a TON of stuff going on with 3.0 that people should know about. I’ll have more on this in the coming weeks.

Related


Gravatar
Steven Harman - Wednesday, June 10, 2009 - to be fair, were I really building out my domain model in an OO fashion, I'd very likely not have a CategoryID on my Product. instead I'd have a reference to the actual Category object, or perhaps no knowledge of the category at all (from the product's point of view), letting the Category manage a collection of Products.
Gravatar
Eric Polerecky - Wednesday, June 10, 2009 - Wow, great stuff rob....Any support for foreign keys?
Gravatar
James Brechtel - Wednesday, June 10, 2009 - This sounds great Rob. I will be using this soon.
Gravatar
ajwaka - Wednesday, June 10, 2009 - Thanks Rob - you make my (work) life easier! Now if SubSonic could only help out with: Baby myThreeMonthOld = new Baby().Sleep("ThroughTheNight").And("Poop").OnlyForMom().Save();
Gravatar
ajwaka - Wednesday, June 10, 2009 - Thanks Rob - you make my (work) life easier! Now if SubSonic could only help out with: Baby myThreeMonthOld = new Baby().Sleep("ThroughTheNight").And("Poop").OnlyForMom();
Gravatar
Joe Brinkman - Wednesday, June 10, 2009 - This is a horrible example of MVC... oh wait... Sorry wrong blog. I can't wait to start playing with 3.0, but after this you must promise to stop breaking things for a while.
Gravatar
KevinPang - Wednesday, June 10, 2009 - Having conventions in your POCO objects drive database design and migration? Very cool and very ambitious. I'm interested in seeing where this will go once you start support edge cases (multiple foreign keys, many-to-many associations, etc.). Or is the goal of the SimpleRepository to only be used for barebones CRUD applications? If so, will it play nice with the rest of the system? The automatic migrations seem like they might be more of a headache than a timesaver if the SimpleRepository had to be used in conjunction with normal SubSonic database access.
Gravatar
Eric polerecky - Wednesday, June 10, 2009 - If I make changes in my model and those changes are pushed down to the database will it update the foregin key mapping as well or does this alpha not support that yet. Either way great work.

Any future plans to hydrate related objects as Steve suggested? I think that would give me the confidence to try this in a production system.
Gravatar
Troy Goode - Thursday, June 11, 2009 - Have you thought about taking advantage of DataAnnotations for your attributes? Several of the attributes you have currently could then serve a second purpose as validation descriptors (such as when using xVal with MVC). I know the DataAnnotations packaged with .Net 3.5 are quite limited, but perhaps you could create custom DataAnnotations now and deprecate them with a SS 3.1 release when .Net 4.0 comes out.
Gravatar
Khalid Abuhakmeh - Thursday, June 11, 2009 - You are sick Rob (in a good way); This approach looks awesome, but I have to agree with Steven Harman about the CategoryId issue. Also what happens when you modify your object, is the simple repository smart enough to change the table? sidenote: I just finished reading your MVC book and it's a must read for any developer. Thanks for the great work.
Gravatar
Mads - Thursday, June 11, 2009 - Will be possible to override the names of the properties and the class?
Gravatar
Mads - Thursday, June 11, 2009 - Looks great btw :) Looking forward to playing with it. What versions of MySQL is supported?
Gravatar
Eric Hexter - Thursday, June 11, 2009 - Love what you got here.. For working with my domain I could really fly not having to worry about my database mappings ... at alll. There is a certian kind of app where this would totally work. I think that there is a certian point at where large applications could start with a low friction persitence while the domain is still being flushed out. Any chance of exposing the conventions in a way that I could provide my own desicions around column type selection? That way I could deal with making anything with the name HTML a nText or anything with the name Description a nvarchar(512) ... ect....
Gravatar
Donn - Thursday, June 11, 2009 - " I think, in general, the .NET crowd overthinks and over-engineers just about everything " - I COMPLETELY concur.
Gravatar
Herbrandson - Thursday, June 11, 2009 - " I think, in general, the .NET crowd overthinks and over-engineers just about everything " - I'm going to have to think about that before I can agree or disagree
Gravatar
Matt Sherman - Friday, June 12, 2009 - Hey Rob, great stuff. Question...in this statement: repo.Add<Product>(newProduct); ...is the <T> clause necessary? Doesn't the repository already know the type based on newProduct?
Gravatar
Liu Peng - Friday, June 12, 2009 - 太牛了,居然能设计出这么好用的东西!
Gravatar
mycall - Friday, June 12, 2009 - var repo=new SimpleRepository(provider,true); ..how about changing the true to a contant or an enum element?
Gravatar
ilog - Friday, June 12, 2009 - I would offer public enum LevelOfTruth { true, partiallyTrue, notCompletelyTrue, canBeTrue, mightBeTue, notTrue, false }; Sorry, cannot restrain... :D
Gravatar
bonder - Saturday, June 13, 2009 - Will you support entity relations expressed in code, such as Product having a member of Category? That would be just the perfect level of over-engineering (a weak and lazy term meaning "I don't understand this therefore it's bad") for me! :)
Gravatar
Bret(runxc1) - Monday, June 15, 2009 - I know that Oracle is a pain but what about us poor folk that have to deal with the pain. What would it take to get Oracle up and running with Active Record or SimpleRepository??
Gravatar
Luiz - Monday, June 15, 2009 - Hi Rob, looks like SubSonic will be a great tool. The key aspect is the simplicity. Simple to configure and simple to use. I like the option to work with repository and active records, this is great. One thing that I missed is the capability to use Unit Of Work with the repository, just like LinqToSql when you do several things in the repository and then "Submit Changes".
Gravatar
nobodybutca - Tuesday, July 21, 2009 - Rob, I would like to thank you first to this wonderful tool. Now my issue: - I am using sql server 2008, to follow the latest convention I used new schema other than [dbo].[tablename] which is now look like this [newdbo].[tablename]. The problem now, SimpleRepository could not locate [newdbo].[tablename], I assumed it is looking for [dbo] rather than [newdbo], since my class is define as: public tablename{}.
Gravatar
Digvijay - Tuesday, July 21, 2009 - Hi Rob, Great work and much more better when i last used subsonic 2.x I had one question tho, how about databinding with nullable types when using SimpleRepository. Is it something that can be addressed in codegeneration or do i have to get better at DataBinding ;-)