Bruce said:
Note that this isn't an O-O problem at all. Once you add in tricks
like caching and prefetching, you have to start wrestling with
problems of data going stale: "If I prefetch / cache, how do I keep
up with changes in the database? How do I make sure that the data
within my application is current with the data in the database?"
I don't think caching and prefetching are related. They're completely
different things, and I don't thing what applies to caching applies to
prefetching as well. UNLESS! you're defining 'prefetching' as 'fetching
data way before it's perhaps needed'. In that case we're talking about
two different things
. I call 'prefetching' the fetch action you're
doing together with another fetch action because you know you'll need
it in the very near future, e.g.: you need both car and driver, thus
you fetch both up front.
Stale data is always a problem where data is consumed outside the
system where it's stored/kept. Though as that's a given, a developer
has to realize that as soon as s/he fetches data from a table / view in
the db, the data IS stale.
Depending upon your application, the answer may range from "I don't
care. I don't need to keep current with changes to the DB," to "I have
to fetch every time because I need to be as current as possible with
the DB." At this stage, the questions and problems are the same
whether you're writing an O-O application or a 3GL application.
true, but that wasn't the OO problem at hand I think. The OO problem
at hand was more in the form of:
- in an OO world you would focus on fetching each Car object
individually and every Driver object individually
vs.
- in a set oriented world, you'd focus on the set of data you have to
fetch, e.g. a joined list or 2 sets with a mapping between them (e.g.
dataset with two datatables and a datarelation).
If you want to do the latter, but want to use OO objects, there's a
friction, because they're not equal.
The difference I would submit, is that O-O requires more careful
design in this regard, as it's harder to immediately understand what
operations will result in database fetches. If you design your objects
(as I did mine) so that a property reference:
Driver d = car.Driver;
may result in a database fetch, it's very difficult to know, looking
at the code, where trips back to the database may occur.
trips back as in, persisting data?
I see it like this: say the algorithm A consumes an X amount of data.
That data is consumed in a period of time P. If it's ok for A that X is
available before P starts, you could opt for an optimization to fetch X
up front, and then proceed into P. If it's not ok for A to have X up
front, you have to fetch X during P, and perhaps the amount you need at
that given moment.
A is then efficient if A has the data amount X' available at time T
when it needs X'. This thus means that when X' is needed, no delay in
fetching should occur. IF fetching-on-demand (lazy loading) is enough
to feed A with X' amount of data at any given time T, why bother with
prefetching? IF fetching-on-demand isn't enough, prefetching is
recommended.
You see, in the statement you gave, it's not important for the
developer what happens below the hood. What's important is that after
that line, 'd' points to the Driver entity of car, as that's what
'reality' is on the abstraction level the developer works on.
This doesn't
make the O-O approach somehow inferior. All it means is that you have
to take more care in architecting your solution. We're using a
horribly slow ODBC connection, and we've managed to optimize our
trips back to the DB. It can be done.
Oh, I didn't want to imply that an OO solution would be inferior, not
at all. I just wanted to imply that the hard-core 'This is not OO and
therefore crap!'-slogans aren't practical to work with
so it should
be a basis to base a decision on.
One change I would recommend is this: don't use a property, use a
method:
Driver d = car.GetDriver();
It should be a method, indeed, because the operation can be expensive
(if the related driver hasn't been read yet) and therefore the MS
guidelines say you should create a method.
The sad thing is though, what would you suggest for the other side of
the relation:
Cars c = d.Cars;
It then should be logical to have a method as well, correct? However,
then you run into the problem with databinding. What if you want to
bind the Cars collection of the selected driver in a grid to another
grid? You can't do that with design time databinding or other simple
constructs, it requires ugly glue code, not something you want to
write, trust me
Therefore, I think a property which calls the method (which is also
there) is best. (I use that scheme). The method then can also have
overloads which accept additional filters and other bells / whistles,
and the property is there for easy access and databinding access.
FB
--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website:
http://www.llblgen.com
My .NET blog:
http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------