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):
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…
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…
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.
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.
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.