Hanalei, Hawaii Tuesday, February 09, 2010

Pass Number 1: ActiveRecordEngine for ASP.NET

missing-link

I have a SubSonic prototype that’s been sitting on my hard drive for a while – something I’ve been meaning to spruce up for a bit and release. As of right now I’m happy I didn’t – the code would be considered SubSonic’s and I’d rather it get pulled into a larger effort (wink wink, nudge nudge… bat over the head).

I massaged the interfaces I’ve been using and in this post I thought I’d lay it out for comment.

missing-link

I have a SubSonic prototype that’s been sitting on my hard drive for a while – something I’ve been meaning to spruce up for a bit and release. As of right now I’m happy I didn’t – the code would be considered SubSonic’s and I’d rather it get pulled into a larger effort (wink wink, nudge nudge… bat over the head).

I massaged the interfaces I’ve been using and in this post I thought I’d lay it out for comment.

Functional Background

The idea is that you “plug in” the ActiveRecord “stuff” to your web app and use  it as you see fit – with perhaps a default being offered from Phil and team (using one of the ORMs their baking up there… :):):). So imagine that, out of the box, in the Global.asax is a line like this:

Controller.SetActiveRecordEngine(new EFActiveRecordEngine());

Yes yes I know. Settle down.

First Pass

To get all of this to work properly we need to define two interfaces – one that works the database, the other that describes the object. I’ve been sort of thinking a lot about this for a long time… can you tell?

Here’s a starter interface for IActiveRecord, with the goal of keeping it lean and simple:

    public interface IActiveRecord {

        object Add();
        int Update();
        void Save();

        void Delete();
        void Destroy();

        string KeyName();
        string KeyValue();
        string DescriptorName();
        string DescriptorValue();

    }

Aside from the typical CRUD stuff, there are 4 methods that allow you to do some groovy UI work here (Key/Descriptor stuff). With these specified we can hijack the HTMLHelpers to do our bidding – things like DropDowns and simpler tables. Heck we could even do Editor<T> if we wanted.

The next part is the engine – the thing that would do the query stuff:

    public interface IActiveRecordEngine {

        T Get<T>(object key) where T : new();
        T Get<T>(Func<T, bool> expression) where T : new();


        IQueryable<T> All<T>() where T : new();

        T Add<T>(T item) where T : new();
        void AddMany<T>(IEnumerable<T> items) where T : new();

        void Update<T>(T item) where T : new();
        void UpdateMany<T>(IEnumerable<T> items) where T : new();

        int Delete<T>(Func<T, bool> expression) where T : new();
        int Destroy<T>(Func<T, bool> expression) where T : new();

    }

The power of Linq means that we don’t need to define much beyond “All()” – aside from the CRUD stuff. This is our answer to “method missing” with Rails’ ActiveRecord. Well at least sort of.

Putting the Pieces Together

Under the hood the vendor who implements these interfaces would be responsible for allowing the developer to harness their particular ActiveRecordEngine to IActiveRecord – meaning that when object “Post” which implements “LinqToSqlActiveRecord” calls “Add()” then the underlying “LinqToSqlActiveRecordEngine” does the heavy lifting.

Your Thoughts Please – and Your Voice

Data access is a pain in the ass and something that we should really move beyond. If this interests you – heckle some people. Twitter it, blog it. Make yourselves heard. Phil is very much into this (it was our convo that sparked this whole thing) and let’s resolve this API so we can (if we so choose) all speak the same data access language when using ASP.NET.

Talk to me! If you want something like this – you need to speak up.


Damien Guard - November 17, 2009 - The ActiveRecord pattern has the get/save methods as static...

[)amien
Neal Blomfield - November 17, 2009 - All looks good *except* for the first line of code. Having a default AR api is great as it simplifies a lot of those simply CRUDdy web applications and makes it easier for developers to start using these kind of approaches ( especially if they are MS sanctioned ), but tying the AR engine to the controller means you are adding persistence concerns to something that should be focussed entirely on application flow.

If you want to expose the engine via the controller without using any form of DI, a cleaner approach would be something like:

public class ARController : Controller
{
public IActiveRecordEngine ActiveRecord { get; private set; }
public void SetActiveRecordEngine( IActiveRecordEngine engine )
{
ActiveRecord = engine;
}

// ... add a bunch of convenience methods on the controller to
// simplify access to the AR engine ...
}

this means if people want to use this approach can by subclassing the ARController and going from there ( e.g. LinqToSqlARController ), however those that don't are not left having to deal with a bunch of additional methods on the controller that they will never use.

This approach also means those people left in webforms land can use a similar approach - by providing a base ARPage with a similar api.
Rob Conery - November 17, 2009 - Yes it does, but since we're harnessing this to our Controller I was thinking it would be an instance we're dealing with. I haven't thought through the nuts/bolts yet - but you're right about the statics.
Rob Conery - November 17, 2009 - Lunar Lander. I want to challenge you (and the rest of the people about to leave comments) to suspend the notion of ActiveRecord as "that cute little thing for really simple apps". It's what makes ASP.NET laughable in the web space - it's just too damn hard and we impose patterning when we don't need to because we're engineers and we want to think we're awesome. And our apps show it.

Your ideas are sound and approach valid. Not terribly useful nor awesome and useful/awesome is what we need or we go on being laughed :).
Marcus McConnell - November 17, 2009 - Looks like a great start. I agree that Rails has an edge for getting a quick and dirty framework running.

Are the KeyName functions designed to get the Primary Key name and Value? I have used Int, Long, GUID, etc. for Primary Keys. Will I need to cast values to implement this interface? Can you give a sample of how it would be used?
Neal Blomfield - November 17, 2009 - I can see where you'd be thinking arch astronaut, but I'm looking at this from the perspective of keeping my controllers focussed on application flow rather than business logic.

My controllers are insanely trivial, everything is baked into tasks ( my actions look something like public ActionResult Login( ILoginCommand command ) { ... } ), all my controllers do is manage application flow. This may sound like an ivory tower approach but it means I can build simple controllers and simple tasks rather than horribly complex controllers that run to huge action methods.

Your concept is great, BUT, don't make me deal with baggage where it is not needed ( i.e. don't impose the AR pattern on me just because I am using MVC ).
Rob Conery - November 17, 2009 - Application flow *is* business logic. It *is* an Ivory Tower approach and that is perfectly fine.

I'm not "making you deal with baggage" - use it or don't, the choice is yours.
Larry Andersen - November 17, 2009 - As long as it's not TOO simple. What you have laid out here is a start, but it's got a long way to go to be useful. For instance, At it's most basic, ordering and paging support should be there. I know you cover this somewhat in SubSonic and it should be applied here. Also, I think it's bad to assume entities will only have a single key field. I know dealing with composite keys complicates things quite a bit, but realistically, and moderate sized app will more than likely have relation tables with composite keys. And finally, I think there needs to be some consideration for lookup values in foreign key tables, especially in web applications where frequently you are dealing with lists upon lists of data and each row you display has to render some friendly name/descriptor from the foreign key table associated with the record. Hope that makes sense.
Rob Conery - November 17, 2009 - Hi Larry - TOO simple is really the mandate in my eyes. And if people say it's too simple then I think it probably needs to get simpler.

Paging support is anticipated with Skip().Take().

Multiple keys are an Anti-Pattern. They will steal your stash in the middle of the night and destroy whatever semblance of sanity you might have started with. There is *no* reason (short of a DBA on acid and a many to many table) to have a composite key. It violates relational design.

Lookup values are not an ActiveRecord concern - this would be a vendor decision.
Benjamin Gram - November 17, 2009 - First off, I love the concepts behind all of this and am very excited to see where it all ends up.

I may be missing something obvious here, but my initial thought is that I wouldn't want my ActiveRecord engine harnessed to my controller. While I agree that controllers (tied with views) manage application flow, I do not always have my persistence buried inside of the controller itself.

It seems to me that I would like to have the ActiveRecord engine be self-contained (or more tightly associated with the model portion of the code-layout) just like ControllerFactory and ViewEngine.

So instead of:

Controller.SetActiveRecordEngine(new EFActiveRecordEngine());

My initialization would be similar to a view engine or controller factory:

ActiveRecordEngine.Engines.Add(new PhoneServicesControllerFactory());
Benjamin Gram - November 17, 2009 - My last line should have read:

ActiveRecordEngine.Engines.Add(new EFActiveRecordEngine());
Rob Conery - November 17, 2009 - The only thing you're missing is the Red Pill. Something to drag you away from the notion that what we're building in some way equates to our mastery of the platform and the field itself. ASP.NET web apps are overbuilt - full stop.

Yes there are times when you might be Greg Young building a Stock Trading application but if that's the case - well you'll be in orbit and you won't care much about ActiveRecord.

I think I might be dreaming - but that's just me I spose. If I had three Geek Wishes they would be (probably in this order):

Convince ASP.NET developers that they're not building Rockets
Convince ASP.NET developers that they're not building Rockets
Convince ASP.NET developers that they're not building Rockets

Should the unfortunate circumstance occur where I actually do meet an ASP.NET developer from NASA then I'm fairly certain a black hole would open up and consume all of humanity - only to be abated by the mass of confusion that is NHibernate.
Mike - November 17, 2009 - Should the engine be set in Application_Start, and not be tied to the controller? Like viewengine, right?
Helen - November 18, 2009 - Hurray! The ideas about programming that are bundled up as best practise did not come down from the heavens on stone tablets. I am in no doubt that there are a lot of really smart people thinking about programming and they're coming up with a lot of interesting ideas. We should listen to them. We should try them. But we also need to question them and see if they actually fit what we're doing.

My biggest problem with the material available on the internet and usergroups in particular is people only talk about the strengths of a particular approach. I really wish there were more people talking about the trade offs between different design decisions and in what circumstances which technologies give the team an advantage. What works well for a bespoke web application might not be the right choice for a shrink wrapped set of components. What is necessary for an application with six screens and 100 users won't be the same for something that's going to run the ticketing system for the Olympics.

At the same time I just want to say I'm glad to be working with a technology with a community that has such a passion for doing things better.
Hernan Garcia - November 18, 2009 - I really like this idea and if we are able to have this backed into MVC somehow, eben an EF implementation :-), that could be fantastic.

I agree with Rob most apps that are written out there are simple, very simple CRUD apps where the use of the Repository, Model, Service, etc. patterns are really no needed.
(Note: Please keep in mind I'm not talking about not applying the SOLID principles here, just over complexity.)

We love Ruby on Rails because we can have an app up and running in no time. We accept the Rails way and we stop worrying about silly things like what framework should I use to do this or that.
Yes, I know you can change the conventions in Rails and when you need your app to grow complexity grows as well.

If we add a pinch of TDD on top of it, refactor our apps when they really need to grow and incorporate more complex patterns should/is trivial.

About multiple field keys, yes I have to work in legacy db's all the time and I found terrible schemas with keys composed of two,three and more fields, but those are not the places where I will use AR, keep your PK simples as well.

We need to keep our tools simple and we need to use the right tool for the right task.
That will make our apps more reliable and easy to maintain.

You cna have all the power tools that you want, and you should use them. But to drive a nail in, a hammer is more than not the right tool to use.

Actually I think of AR as a neumatic nailer, faster than a hammer but not much more complicated and it needs the nails to comply with certain specifications.
Hernan Garcia - November 18, 2009 - I'm really sorry about the construction analogy. On my defence I'm doing a lot of demo in the house.
Scott - November 18, 2009 - I've been waiting for something like this for a long time. Building most apps should be *simple* - the data access problem should have been solved a long time ago.
Eric Polerecky - November 18, 2009 - Since day one, and still today, ASP.NET MVC has had critics who compare it to rails. Now that the community (or just you) are starting to built out more rails like functionally you get backlash from....I don't know...people that don't like rails.

Keep your head up.

I hope, like the common service locator for IoC, that vendors and oss projects pick up on this.
Troy Goode - November 18, 2009 - Hi Rob, what is the int being returned by the Update method?
alwin - November 18, 2009 - What is the difference between Delete and Destroy? What should be the difference betweeen Save and Update?

public interface IActiveRecord
{
// insert or update into db, sort of like SaveOrUpdate in NH
void Save();

// delete from db
void Delete();

// primary key in db, how to set the id is up to the AR engine
object Id { get; }

// not sure what you would want with these two if you got Id property
//string KeyName();
//string KeyValue();

string DescriptorName();
string DescriptorValue();
}

And then static class ActiveRecord

// in global.asax
ActiveRecord.SetActiveRecordEngine(new EFActiveRecordEngine());

// in the controllers: utility methods to access the methods of the AR engine
ActiveRecord.Get(object id)

Have you looked at Castle's ActiveRecord? It works pretty good, but it's still somewhat complicated. At least it was for me when I just started without ever using RoR before. But now I use it with great pleasure.

How would you handle validation? auditing? unit of work?
Kijana Woodard - November 18, 2009 - +1 on combining Update and Add into Save. It seems like something that shouldn't be an issue I have to worry about. I was so happy when I got to try out SubSonic3 and I could do something like:

if (record.IsDirty || record.IsNew)
record.Save();

Now if we could only get rid of the "if" and do something like

repo.Save(records);

Maybe the same applies to Delete/Destroy. By default, If I have a IsDeleted, DeletedBy, DeletedDate type setup in the db, do the logical, else do the destroy. If I care on a table by table basis, I can override methods in the ARE, but MVC, IActiveRecordEngine doesn't bother with those details.

I like Simpler too. :-)

p.s. Rob - You are my hero. Thanks for SubSonic. Changed my life.
Let the AREngine figure out dirty/new insert/update junk. If the AREngine wants to put in a hook to let me override that stuff, fine. Most of the time, it's just the same logic every time.
Kijana Woodard - November 18, 2009 - Strange...the "Let the AREngine figure out..." paragraph got moved to the end of the post from the middle.
When's Hana coming out. ;-)
Kijana Woodard - November 18, 2009 - I get both points.

So here's a simple solution. If I care...I mean really, really care, I can change:

Controller.SetActiveRecordEngine(new EFActiveRecordEngine());

to

//Controller.SetActiveRecordEngine(new EFActiveRecordEngine());

And then set my ARE up anywhere I like. But by default, all the DB stuff just works.
Karl - November 18, 2009 - I may be misunderstanding, but it seems wrong to me that the controller would be "harnessed" to the persistence implementation. RoR/Django/Cake have the right idea by making that an implementation detail of the Model.

Get should be static. PHP added late static binding to 5.3 specifically to support that kinda feature. Existing PHP MVC frameworks which don't leverage 5.3 (most of them) have really ugly/stupid syntax for fetching.

The AddMany and UpdateMany seem redundant. You can handle that via overloads or params. Unless I misunderstand the purpose, UpdateMany on an instance seems weird.
Rob Conery - November 18, 2009 - supposed to be Number of records changed - that shouldn't be on the single method though. Typo!
Rob Conery - November 18, 2009 - Delete can implement logical deletes while Destroy always removes the record.

It's been my experience that people need a "back door" to explicitly call Add/Update when Save doesn't do what they want. It happens.

You handle validation/auditing; Unit of Work doesn't really fit AR.
Rob Conery - November 18, 2009 - Hi Karl - the reason I stuck it on Controller is simply because that's where you'd use it. RoR/Django/Cake use languages that allow you to do lots of gymnastics (Ruby, Python, PHP) and we have C#/VB. I get your Twitter point RE IronXXX - but that's not realistic just yet.

Yes Get should be static - but as far you know it will be if it sits instantiated in the Controller. The idea would be this:

Controller.Model.Get(x=>x....);

In this case "Model" was created when the Controller was.

AddMany/UpdateMany could indeed be SaveMany, but as I mention to alwin it's been my experience that people need a back door when the tooling doesn't do what's wanted.
Kijana Woodard - November 18, 2009 - I was about to make a point similar to Karl's. Instead of AddMany, why not just Add?

void Add(T item) where T : new();
void Add(IEnumerable items);

I suppose add returned your new "T". I guess that's so you can get the id back from the db?

In terms of the "back door", can't the ARE worry about that. To make things as simple as possible, IActiveRecord could just specify Save and if the ARE implementation wanted to give outs for Add/Update it could do that and the programmer could go deal with it there.

I always get confused when I see all three and wonder if I'm doing something wrong. So far, with SS3, I just use Save and couldn't be more satisfied.
Kijana Woodard - November 18, 2009 - seconds Add was supposed to be:
void AddMany(IEnumerable items) where T : new();
Kijana Woodard - November 18, 2009 - seconds Add was supposed to be:
void Add(IEnumerable items) where T : new();
Kijana Woodard - November 18, 2009 - I'm starting to see why you're building your own blog. Sorry about the multi-posts.
Kijana Woodard - November 18, 2009 - Not sure how to get the formatting right for the generics on the blog comment, but here's another proposal if the T returned from add was so that you can get Id numbers back from the DB:

T Add(T item) where T : new()
IList Add(IEnumerable items) where T : new()

or, since I would rather simplify the interface and eliminate Add/Update

T Save(T item) where T : new()
IList Save(IEnumerable items) where T : new()
Mark Brackett - November 18, 2009 - > Multiple keys are an Anti-Pattern...[they] violate relational design.

You might want to back up that statement, since AFAIK composite primary keys are not only sound relational design - but actually tend to make *better* relational designs. Folks who don't like composite keys generally don't put a unique constraint on them either - and end up with duplicated data.

Now, I won't argue that composite PKs makes the job of an ORM harder. That's an ORM problem though, not a relational one. In most cases, I'm more than happy to trade my natural composite PK for a surrogate to make my coding e easier - but (and this is the bit that most composite key hating folks forget) you *MUST* put a unique constraint/index on that composite key anyway! Otherwise, you *are* violating relational design!
Rob Conery - November 18, 2009 - Back what up? It's basic (aside from many to many - and even then there's a strong argument for a single PK).

PK's should have nothing to do with the data they contain - they should identify a unique, non-repeating set of data. If you combine two columns and say "there - that defines the row" then what you've done is impose external logic that may change and screw your keys up later.

So back at ya - give me a good reason to use that over a single key field.
davethieben - November 18, 2009 - i'm sort of a LINQ n00b, so excuse the question - but if I do:
engine.All().Skip(500).Take(10).ToList()

does the trip to the db return all records, and then the app filters? or does that statement get optimized to only return the 10 records?
jdn - November 18, 2009 - Oh, dude....You do not want to get into that religious war...LOL.

Anyway, google 'Joe Celko' on what a primary key should look like. Total opposite of what you are saying.
Rob Conery - November 18, 2009 - LOL Joe Celko! That's hysterical! You mean this Joe Celko:

" He is often corrected by product experts including a number of the SQL Server MVP's which lead to protracted discussions which more often than not he comes out losing"

http://en.wikipedia.org/wiki/Joe_Celko

And you're right - I don't want to get into this debate. DBAs can have this debate and once again I'll invoke Lunar Lander.
jdn - November 18, 2009 - You seem to have turned off the reply ability, or my browser sucks, so this will probably appear out of order...

Anyway, yeah, 'that' Joe Celko. As someone doing SQL DBA work for 10+ years, I think, maybe about 5 years ago, I would have been one of those "Oh, God, Joe Celko?" type people.

Except I was wrong. Not that I necessarily agree with everything (or even a majority of what) he says, but after more experience and being humbled more, I understand where he is coming from, and what Mark says is correct. Composite primary keys, in many instances, produce better relational designs.

Since it doesn't matter anyway, and we can't really resolve this in blog comments, I would just say you should hesitate in being that confident in saying things like "PK’s should have nothing to do with the data they contain" when that isn't really true.

FWIW, YMMV, etc. etc. etc.
karl - November 18, 2009 - It would be up to the implementation, but I'm sure Rob is thinking that it would be applied as an optimized select (as it is in Linq2SQL or Linq2NHibernate)
karl - November 18, 2009 - still don't see why it's in the controller.

You should be able to do (without any dlr magic), things like:

var user = User.Get(u => u...);
user.Save();

I think our approach may be different in that you see this as a 1-model-per-controller. To be honest, I don't think that's actually how the ror/django/cake folk do it..and I think they are right in not doing it. I think a model-per-action is much more common and usable.
karl - November 18, 2009 - This is an active record implementation, not an ORM. The only supported primary key should be a surrogate. You can still (and should) let the db manage unique constraints.
Javier Lozano - November 18, 2009 - I can haz codez? :)
Rob Conery - November 18, 2009 - I didn't turn it off - for some reason it's whacking out today. Anyway - keys uniquely define a row of data - we know that much. All data in the row needs to be unique according to the key - in other words everything stems from said key. I think we'd both agree.

The debate (as you've pointed out) comes in when someone says "my key means nothing!" and decides that an OrderDetail should be defined by UserName/SKU/OrderID. Why yes - those three things could be used to uniquely define that line in the order. So could a single integer called OrderDetailID.

The difference is to the DBA/Designer. One has meaning, the other doesn't. It has meaning because the DBA gives it meaning - to himself. It's not a mechanical meaning, it's a semantic meaning.

Those don't belong in a DB design.

Now I also think you're right - I probably shouldn't make a statement so strong so let's ammend it :). Given the choice - Composite Keys are an Anti Pattern :). How's that? I do know that there are times when you need to use them...
jdn - November 18, 2009 - There is still some weirdness with the nesting of the comments, but cool. Manageable enough.

OrderDetailID can never match UserName/SKU/OrderID as a primary key, precisely because it is just a random ID. The gist (I think..LOL) is that the latter stays constant across any DB implemtation, while the former depends on the particular DB implementation.

Let me put it this way. Semantic meaning is more important than mechanical meaning, but mechanical meaning is a hell of a lot easier to implement. When implementing SQL Server database designs, my immediate inclination is to implement a primary key that is meaningless. Ignore why this is my immediate inclination for a moment if you can. From both a DB design perspective and a DDD perspective, I think this immediate inclination is probably wrong.

It's like the debate between using int as your identity key vs. using a GUID. Up until (something like) two years ago, I would have insisted you had to use int, that using a GUID was stupid. Knowing what I know now, I know GUID is actually better, based on what Greg/Udi have experienced.
gunteman - November 19, 2009 - Thanks for saying this. It needs to be shouted out from roof tops over the world. So far, ASP.NET has given me a lot of work a quite a bit of joy. But has it made me more efficient and increased the quality of my applications (as opposed to working with PHP, which I did before) ? Not really. It's partly the fault of the framework, more so the fault of the patternoholic community and of course mostly my own fault. But I'm beginning to see the light.
alwin - November 19, 2009 - Yeah I guess Delete/Destroy comes from the ruby world where it may be important, but for me it's just another 'which one do i pick' option. I only used Castle AR where there is no Destroy, just Delete (with does the same as ruby Destroy AFAIK). I still don't see the use for both.
Is a logical delete just a IsDeleted = true column?

How much is the Add/Update back door needed? With IActiveRecord, only Save is needed where the engine figures out if it does Add, Update, Save, Insert, etc. Is it enough if users can do explicit Add or Update through the IActiveRecordEngine?

I say put the back doors on the engine, and only the main entrance on the IActiveRecord interface.
alwin - November 19, 2009 - I agree with Kijana Woodard about the back doors.

It took me a while to figure out the difference between Save and Update in NH, and I still don't fully understand the diff between Delete and Destroy. For a noob like me, it is not simple enough.

Rob, you said yourself "Hi Larry – TOO simple is really the mandate in my eyes. And if people say it’s too simple then I think it probably needs to get simpler."

If you want it to be as simple as possible, only 1 method to persist the AR to the DB should be sufficient: IActiveRecord.Save(). Any back-doors needed should IMO be in IActiveRecordEngine, to keep IActiveRecord as clean and simple as possible.
alwin - November 19, 2009 - var user = User.Get(u => u…);

That's more or less how Castle AR does it. Query methods are accessible through static methods on either the AR base class or AREngine.

Not tied to a controller, but still very simple :)
Kevin Southworth - November 19, 2009 - YES! A common interface for doing data access in ASP.NET would be fantastic! I know MS likes to leave it wide open so that any data access can be used, but I have long wished there was a more opinionated approach to data access in ASP.NET. There should just be "the way". Rails, django, etc. have adopted this approach and even though their bundled "ORMs" might have drawbacks, at least everyone speaks the same language and it's easier to get going with those frameworks because of it.
Ruairi - November 19, 2009 - I do have a concern about fat a controller layer. If you allow
IQueryable All() where T : new();

Then all hell can break loose in the controller. Am I missing the point?
Jason - November 19, 2009 - "...only to be abated by the mass of confusion that is NHibernate."

Please explain :)
paul - November 19, 2009 - Rob, maybe I just haven't had enough kool-aid (or maybe too much...) but I still don' understand what makes the AR style more simple or more useful than having a generic repository. I mentioned it on your other post, too, and I just don't get it. If anything a generic repository is simpler, because my model objects don't have to implement anything if I don't want them to (though an ID property is likely just good sense).

Example: IRepository looks just like your IActiveRecordEngine (or very similar). We're being Opinionated, so we assume that all IDs are ints, or else we make one extra interface that folks can use that are IRepository or similar.

The overall implementation could be set the same way, and controllers could merrily call its methods to do their work.

Entities could be anything, w/o having to implement hte interface you describe, making them simpler and easier to test/debug.

Query encapsulation (e.g. GetCustomerByName("jones")) could be handled quite easily through extension methods.
Rob Conery - November 19, 2009 - What's "fat" about it? "All hell can break loose"?
Rob Conery - November 19, 2009 - Compared to Rails ActiveRecord, NHibernate is utter silliness.
Rob Conery - November 19, 2009 - IRepository is pretty simple - but it's just not needed most of the time. In fact Ayende wrote an interesting post that I happen to agree with: The Repository Pattern is Dead.

It's not that it's bad - it's the ORMs are that much better. The pattern was developed a long, long time ago to shield SQL from the developer - makes sense. But ORMs these days are really quite smart - SubSonic can mock itself, NHib can too.

Anyway - the one thing that makes AR a bit simpler is
post.Save();
vs
PostRepository.Save(post);

It's one less "moving part".
Anders - November 19, 2009 - Have you seen the Active Record from MS demoed at PDC? http://microsoftpdc.com/Sessions/FT18 at about 45 mins in
justin - November 19, 2009 - I'm going to have to disagree here. Your IActiveRecordEngine is basically just a repository, at least...I can't see a difference. The pain point in your plan is implementing the IActiveRecord interface on each model.
Gecko