I just got out of this session and am writing this from the bus on the way back to
my hotel. From there we are off to Universal Studios for the attendee party. They've
rented out the entire place for just us! Kick ass.
If you've read my blog for long, you've probably heard me rave about code generation
tools like CodeSmith and LLBLGen. I've written a number of Object Relational Mapper
(ORM) tes in CodeSmith and I've demoed LLBLGenPro. Both techniques work well, but
I've always wanted something built into the framework. The ones I wrote were specifically
for my needs and didn't support things like relationships, lazy loading of child relationships,
etc. LLBLGen supports some of that, maybe all, but it is still essentially a code
generator.
I've been hearing about ObjectSpaces for a while now but it has been very hard to
get any information about it. And with all the hype about the other Whidbey and Longhorn
technologies, it seemed to be ignored by the community.
ObjectSpaces is three things:
1. System.Data.ObjectSpaces is the namespace containing the core ORM subsystems in
Whidbey
2. Microsoft.Data.ObjectSpaces (or something like that) contains utilites for generating
databases from object graphs and vice versa
3. VS.NET (Whidbey) will have a very cool editor for mapping table and columns to
objects and fields.
This stuff appears to be exactly wat I was hoping for. I tried my best to copy the
code from the demos below, and for this kind of this the code is the best way to see
what is going on. I cannot guarantee that this code is correct, and it certainly will
not compile, as I was typing it in on the fly as the presenter switched between slides
and code. But it is close enough to give you an idea of what I was seeing. Unfortunately
I didn't catch all of the code for ObjectSpaces -> ADO.NET interop (case 5), but
I'm sure that will be pretty straightforward. This is the Good Stuff.
Look in the switch statement to see how database initialization, reading and then
updating work. This stuff is VERY cool and is going to save us business app developers
TONS of time and code.
Enjoy!
using Microsoft.Data.ObjectSpaces;
using System;
using System.Collections;
using System.Data.ObjectSpaces
publicclass Continent
{
public Continent(string name, int area)
{
Name = name;
Area = area;
}
publicstring Name;
publicint Area;
public ArrayList Countries = new ArrayList();
// Required? for object spaces private Continent();
}
publicclass ContinentDelayLoad
{
public Continent(string name, int area)
{
Name = name;
Area = area;
}
publicstring Name;
publicint Area;
public ObjectList Countries = new ObjectList();
// Required? for object spaces private Continent();
}
publicclass Country
{
public Country( string Name, int pop,
Continent cont ) { ... }
publicstring Name;
publicint Population;
public ArrayList Cities = new ArrayList()l
public Continent Continent;
private Country() {};
}
publicclass City
{
public City() string name, int numberOfCats,
Country myCountry )
{
_Name = name;
NumberOfCats = numberOfCats;
Country = myCountr;
}
privatestring _Name;
publicstring Name
{
get { return _Name;
}
set { _name = value; }
}
publicint NumberOfCats;
public Country Country;
private City() {}
}
publicclass WorldDriver
{
staticvoid InitializeDatabase()
{
Continent asia = new Continent("Asia", 17110 );
...
Country china = new Country( "usa", 111, asia );
...
City beijing = new City( "Beijing", 1112, china
);
...
// This is not part of ObjectSpaces per se, it is used
to generate a database based on a graph. If we already have a database // we don't need to do this. This is NOT part of the System.ObjectSpaces
namespace.
ObjectPersisitence op = new ObjectPersistence( "Data
Source=(local); Integrated Security=true;", "World"
);
Print("Persisting Asia");
op.Persist( asia );
op.Persist( asia.COuntries );
foreach( Country c in asia.Countries
)
{
op.Persist( c.Cities );
}
}
staticvoid Main( int argc, char[]
argv )
{
int whichDemo = Int32.Parse( argv[1] );
switch( whichDemo )
{
case 1: //Build database
InitializeDatabase();
break;
case 2: // Load graph of objects
os = new ObjectSpace("World.msd.xml", cn);
ObjectSet oset = os.GetObjectSet( typeof(City), "", "" );
// Using span to get early loading (these load a predefined
amount of the object graph) // ObjectSet oset = os.GetObjectSet( typeof(Continent),
"", "Countries" ); // Tells it to load the countries too! // ObjectSet oset = os.GetObjectSet( typeof(Continent),
"", "Countries.Cities" ); // Tells it to load the countries and the cities!
Print( oset );
break;
case 3: // Update an object graph (optimistic concurrency
-- first save wins)
os = new ObjectSpace("World.msd.xml", cn);
City gt = (City) os.GetObject( typeof(City), "Name='Absiola');
Print("Got it");
// Modify the object
gt.NumberOfCats = new Random().Next();
// Save it try
{
os.PersistChanges(gt);
}
catch( Exception e )
{
os.Rollback();
}
break;
case 4: // Update an object graph (with transactions second
to read will wait for the first to commit or not before saving)
os = new ObjectSpace("World.msd.xml", cn);
os.BeginTransaction( IsolationLevel.RepeatableRead);
City gt = (City) os.GetObject( typeof(City), "Name='Absiola');
Print("Got it");
// Modify the object
gt.NumberOfCats = new Random().Next();
// Save it try
{
os.PersistChanges(gt);
os.Commit();
}
catch( Exception e )
{
os.Rollback();
}
break;
case 5: // ADO.NET Interop string cs1 = "DataSource=(local);Integrated security=true;...";
string query = "select top 3 * from coutnry";
SqlCommand cmd = new SqlCommand( query, new SqlCOnnection(cs1)
);
SqlDataReader r = cmd.ExecuteReader();
MappingSchema map = new MappingSchema("Woeld.msd.xml");
DbObjectReader reader = new DbObjectReader( r, typeof(
) );
// Didn't catch the rest of this but you can imagine what
is going on here...
break;
}
}
staticvoid Print( string s
) ...
staticvoid Print(
IList c ) ...
staticvoid PrintContinents(
IEnumerable e ) ...
staticvoid PrintCountries(
IEnumerable e ) ...
staticvoid PrintCities(
IEnumerable e ) ...
}