March 2004 - Posts
My previous blog entry raised some comments I´d like to respond to.
Am I proposing just an ordinary projection feature for ObjectSpaces (OS)? I don´t think so, because with a projection in SQL (or relational databases), you get different results when you issue select colA, colB from myTable vs select colA from myTable. Hence you need a generic data structure like a cursor (e.g. a ADODB.RecordSet) or a ADO.NET DataTable/DataRow to accomodate for the different number of columns.
With OS (or any O/R-mapping (ORM) tool), though, generic data structures are less important. That´s the purpose of ORM. With ORM you define a persistent class like
class Customer
{
public string id;
public string name;
public string city;
public string zip;
}
and hopefully there is also a corresponding typed collection for each persistent class, e.g.
class CustomerCollection : IList
{
...
public Customer item(int index) {
get {
...
}
}
...
}
More you don´t want. For a given entity (e.g. customers in some database table) you don´t want to define more than one persistent class.
Then, when you query for customers, you define a selection of entities by formulating a condition on their fields/columns, e.g.
select ... from tbCustomers where name like 'a%'
The collection class represents the result of this operation.
But also, you define how much data to load for each entity. O/R-mapping tools usually support at least two modes: The default is ("full mode"), you load all columns of a table and create a full blown persistent object for each entity returned:
select * from tbCustomers ...
Or you specify to only load "hollow object"s ("hollow mode"), i.e. only IDs of entities matching the query criteria:
select id from tbCustomers ...
Object creation then is delayed until you actually access an object (thru the collection class). Of course, then a specific query has to be issued to load the data:
select * from tbCustomers where id="..."
"Hollow objects" reduce the memory footprint and the amount of data transferred initially by the query, but cause additional roundtrips later on.
From the outside, though, what is returned from a query are always fully populated persistent objects. The delayed loading of "hollow objects" is transparent to client code of the ORM API.
Now, what I´m proposing is a third mode ("sparse mode") of retrieving persistent objects. I propose to be able to load sparsely populated objects by issuing a query specifying a subset of columns/fields to be returned for each entity, e.g.
select id, name from tbCustomers ...
From this data persistent objects are created, but of course not all their fields can be populated. But from the outside, when getting a persistent object from a collection, it still is an object of the persistent class with all its fields/properties - like with "hollow object"s. There is no perceivable difference between an object loaded in first or second or third mode.
And that´s the reason why I rather would not call it projections what I´m proposing - although a SQL projection query is underlying this third mode.
I´d call it "sparsely populating persistent objects", because there still is just one persistent class for a persistent entity, but which sometimes is populated with more column data from the database, sometimes with less.
These kind of "sparse objects" cause less memory footprint than objects loaded in "full mode" or "hollow mode", because in both modes, all columns' data is loaded, before you can access any field/property of a persistent object. Both modes only differ with regard to the time, when the data is loaded. In "sparse mode", though, maybe more than the loaded columns are never needed. "Sparse mode" thus combines the small number of roundtrips of "full mode" with a much smaller mem. consumption.
Like "hollow objects", though, "sparse objects" always look fully populated, because when you access a property, whose data has not been loaded by the initial query, the missing data is fetched (preferrably all missing columns). The query could look like this when accessing the city field/property for the first time
select city from tbCustomers where id="..."
and only load the missing column or columns. Or it could look like this:
select city, zip from tbCustomers where id="..."
and load all columns missing so far. Or it could look like this:
select * from tbCustomers where id="..."
to refresh the object´s data.
Of course this is an additional roundtrip to the database, but then it´s hopefully only rarely necessary, because the fields/properties most often needed in a particular context are specified with the query.
The "sparse mode" thus combines small mem. footprint (thru only needed fields/properties populated) with high performance (thru rare roundtrips) and full read/write access to all fields/properties when needed (thru transparent delayed loading of missing columns).
On the outside it´s still just one persistent class. But on the inside it´s more flexibility than with just "full mode" or "hollow mode". With "sparse mode", "full mode" as well as "hollow mode" are only special cases. "Full mode" loads all columns and never needs an additional roundtrip to the database, "hollow mode" always causes a roundtrip for each object.
When to use which mode? Use "full mode" in editing scenarios, where a persistent object maybe is displayed for modification. Use "sparse mode" in read-only or read-mostly scenarios (but also, when just a couple of fields/properties need to be edited).
Never use "hollow mode"! There is no use for it. Or maybe there is? It´s not important, since "hollow mode" comes free once the "sparse mode" is implemented.
The implementation of "sparse mode", however, requires access to missing fields/properties can be detected. Thus it requires all persistent fields to be encapsulated by property methods for access interception. This is obviously not necessary for "full mode", but also not for "hollow mode". In "hollow mode", only proxies are loaded initially, which cause the (transparent) delayed load of the full object.
Now, since property methods are needed for "sparse mode", ObjectSpaces cannot currently support it. To require properties is against its vision of making an arbitrary class persistent.
But then, as you might know by now or have guessed: To be able to provide object persistence for any class is a lofty goal - and in my view not important to reach for many, many scenarios. Many developers could live without it, and would be happy to define their persistent classes in some special ways (e.g. by deriving from a persistent base class, annotate them with attributes, or model them with a tool or language). Developers are mostly not concerned with the purity or generality of a solution. That is not to say, that sometimes the very general approach of OS is just what a project needs. But at least the companies I´m talking to don´t need such generality.
Matt Warren has provided a look behind the scenes of how features of ObjectSpaces (OS) come into existence in his blog entry "ObjectSpaces: Spanning the Matrix". The entry plus the comments are an interesting read in that they show, how technology features are dependent on single people who advocate them, and how Microsoft watches the market of competive products and needs of developers. Good to know, in the end it´s all just humans at Microsoft :-)
Concerning the feature in question - spans in OS - I of course like them. Passing span information to a query is a very good idea. But they only describe, how deep you dig into the graph of objects for a given use case.
What is lacking but I also find necessary is, to describe how much data should be loaded for each object in the graph retrieved!
Example:
Customers -> Orders -> Items
Scenario 1: List Customers and Orders.
Scenario 2: Edit Order.
The object graph is static. The relationships between the tables/classes do not change.
But the usage of the classes is different.
In scenario 1 I´d like to load maybe only Customers(id, name, city, zip, contactname) and Orders(id, customerid, orderno, amount).
In scenario 2, though, I´d need to load Orders(id, customerid, orderno, amount, order date, shipping date, shipping info, etc.) and Items(id, orderid, qty, price, description, etc.), Customers(id, name, city).
OS' spans solve the problem of defining whether to load Orders or Items at all when running a query.
But they don´t solve the problem of different needs of data population of each object. OS only offers all or nothing with delayed loading. That´s not enough!
In scenario 1 I neither want a "hollow object" for each Order, nor do I want the complete object. I just want enough properties populated to be able to show a list of orders (and customers) without additional database roundtrips.
This feature I haven´t found in any O/R mapping tool yet. And i don´t know why. (But I´m open to enlightenment from any O/R mapping tool manufacturer.)
It seems so obvious to me. I´ve implemented it once in my own O/R mapping tool back in 2000 with ADO - and it was very (!) convenient to use. Unfortunately, since then I had not the time to redo it in the .net world :-(
So my suggestion for OS would be: Allow for groups of persistent object properties. Here´s some pseudo code:
Class Customer
id (*)
companyname (*)
city (list, edit, phonelist)
zip (list)
phone (edit, phonelist)
orders
End Class
Class Order
id (*)
customerid (*)
orderno (list, edit)
orderdate (edit)
items
End Class
Fields "id" and "companyname" are always retrieved. "city" when fields of group "list", "phonelist" or "edit" are requested. "zip" only for group "list". "phone" only for "edit" and "phonelist".
When retrieving objects you could annotate the spans with group info, e.g.
GetObjectSet(gettype(Customer), "companyname like 'a%'", "list", "orders(list)")
Each level in the object graph retrieved would then contain enough information for the current use case, or at least maybe 95% of a use case. Each object would only be sparsely populated. But if, while accessing the objects in the graph, code wants to read a property not retrieved, OS transparently could go back to load missing data.
Advantages of field groups:
-no roundtrips: all data needed for most usual processing within the context of a use case is present. no additional roundtrips needed - most of the time.
-dynamic: data needed is specified at runtime where it´s needed.
-transparent: if data is missing, it is transparently retrieved by OS. Performance would depend how well field groups are designed and used.
Disadvantage: Even though I think this feature is necessary, I doubt, that OS can implement it easily. It would require that an object can check on property/field access, if the data has beend loaded - and if not, go back to the database and get it. This would require field access interception. And that would violate a premise of OS: Any class can be made persistent. Field access interception would mean IL code enhancement or at least property methods.
But then: You can´t have the cake and eat it. If convenience and performance are important, maybe this OS premise should be dropped? Is it important to be able to make any class persistent? I don´t think so - as I have stated earlier.. I deem it more important to have an easy to use programming model.
Microsoft might have thought: "Hey, there are so many relational databases out there. And, hey, people have defined so many classes to represent database entities in their software. To servce them well, we need to provide them with a mapping tool between the existing data models: persistent data model, OO data model."
Sounds plausible to me - from Microsoft´s point of view. But then, the real world is different, I guess. Developers using Microsoft technologies have come up with sophisticated object models much less often, than their colleagues in the Java world. For several reasons. Two being: Microsofts long standing advocacy of data binding to generic container data structures (e.g. ADODB.Recordset and ADO.NET DataSet), and VBs lack of OO concepts for years.
So I´d say, Microsoft´s vision of OS is an answer to a non existing problem. Of course there are huge amounts of existing relational DBs. But there are not that many existing object models for them. Hence, there is not need for a mapping like OS offers. And hence persistent classes could be defined in any way - that makes using them in the end easier.
Which again brings me to graphical modelling tools or domain specific languages for defining persistent classes. With them, implementing field groups would be no problem, because no existing classes needed to be kept and served.
Folge 5 von dotnet.tv zum Thema "Code Access Security" ist abgedreht. Und natürlich habe ich wieder in einigen Postings unsere Abenteuer dabei beschrieben. Eine Übersicht der Drehtagebucheinträge findet sich hier:
http://weblogs.asp.net/ralfw/category/2141.aspx
Viel Spaß beim Lesen!
Und immer wieder gibt es etwas neues zu lernen bei dotnet.tv, zum Beispiel, wieviel Aufwand hinter schnell hingeschriebenen Sätzen stehen kann.
Gestern haben wir diese Szenen geschnitten (wg. des Überraschungseffekts habe ich sie hier ein klitzekleinwenig gekürzt :-)
§ An der Bar
§ Sprecher, Partnerin
Sprecher und Partnerin sehen sich, kommen sich näher, ganz verliebt, gehen zusammen aufs Zimmer.
§ Auf dem Zimmer
§ Sprecher, Partnerin
Sprecher und Partnerin kommen sich immer näher. Er will ..., sie ..., er erschrocken/angeekelt, sie sagt .... Er ... und geht.
§ An der Bar
§ Sprecher, Partnerin
Sprecher und Partnerin sehen sich. Er signalisiert ihr Interesse, sie ist erfreut – aber .... Er lehnt dankend ab.
Der Dreh dazu hat ca. 1 Stunde gedauert. Im Video sind es knapp 1,5 Minuten mit erläuterndem Text.
Aber jetzt: Geschnitten haben wir daran 2,5 Stunden!
Puh. Das hätte ich vorher nicht gedacht. Dass so allgemein gehaltene Regieanweisungen mehr Aufwand beim Drehen und Schneiden bedeuten, war mir schon klar. Aber dass es so lange dauern würde... hätte ich dann doch nicht gedacht. Am Ende war es aber durchaus plausibel und wir haben auch nicht getrödelt oder Patzer gebaut. Die Sichtung des Materials und die Zusammenstellung, so dass es eine Geschichte für den Zuschauer ergibt, braucht halt Zeit. Ein Film mit einer Story entsteht halt doch letztlich beim Schnitt.
Und auch noch an einer anderen Stelle musste ich viel Aufwand investieren: bei den Animationen:

Grundsätzlich sind die Animationsfeatures von Powerpoint erstmal ausreichend, würde ich sagen. Aber im Detail ist die Bedienung nicht für alles optimal. So habe ich für die Animation, die über eine Erklärung von knapp 2 min gelegt wird, gestern 3 Stunden gebraucht. Ich weiß auch nicht, wohin die Zeit gegangen ist. Aber wenn halt jeder kleine Fitzel bewegt werden soll, Farben gesetzt werden müssen, Motive zu finden sind... dann dauert es halt. Die 2 min Text sollten ja auch mit halbwegs belebten Bilder unterlegt werden und nicht nur mit einem Screenshot.
Also, Lektion gelernt. Für Animationen mehr Zeit einplanen, wenn sie so einen prominenten Platz wie in dieser dotnet.tv Folge einnehmen. Und beim Drehbuch genauer hinschauen, wenn Spielszenen nur knapp beschrieben sind. Denn dann hat die Kamera viel Freiraum, um Material zu sammeln, das im Schnitt wieder zusammengeführt werden muss.
Wie könnte es anders sein im "Filmgeschäft", als dass auch Samstag und Sonntag
im Zeichen der Produktion stehen? Seufz... Aber naja, auf dem Weg zu den Oscars
darf einem keine Mühe zu groß, kein Termindruck zu hoch sein. Also: Per aspera
ad astra! ;-)
Zum Teil war es schon vorauszusehen gewesen, dass wir auch am
Wochenende würden reinhauen müssen, da Sebastian in dieser Woche für einen Monat
in Ausland muss, um mehrere Workshops zu halten. Aber dann hatten wir doch
gedacht, wir würden am Wochenende nur gemütlich schneiden.
Unwägbarkeiten bei Drehorten und Mitwirkenden haben diesen schönen Plan
jedoch vereitelt. Für eine Szene brauchten wir z.B. Mutter mit Kind. Die zu
finden, war nicht so leicht. Und so mussten wir schon mit dem Schneiden
beginnen, ohne alle Szenen im Kasten zu haben.
Am Samstag wurden wir dann aber fündig. Die Mutter wollte zwar nicht vor die
Kamera - dafür aber dahinter. Aber die Kinder haben gern mitgespielt:
Am Sonntag wollte ich dann vormittags die Bildschirmszenen, d.h. graphische
Animationen, fertigmachen. Leider geriet das jedoch sehr aufwändig. Animationen,
die in meinem Kopf recht klar abliefen, waren nicht genauso klar und einfach
umzusetzen. Mit Powerpoint als Animationstool sind dann doch irgendwann Grenzen
erreicht. Nach 4 Stunden hatte ich dann nur 4 von 10 Animationen fertig - und
wir mussten wieder ans Schneiden gehen.
Aber auch wenn die Animationen noch nicht fertig sind, so kristallisierte
sich doch eines heraus: die Common Language Runtime (CLR) tritt jetzt endlich
aus ihrer Anonymität heraus und hat ein Gesicht :-)
Mein .NET Twins-Bruder
Christian Weyer verleiht der CLR einen respektvollen Auftritt, finden wir :-)
Donnerstag war für Sebastian und mich ein besonderer Tag: Ganz inspiriert vom gewaltigen Oscar-Erfolg von HdR3 haben wir gleich zwei Videos gleichzeitig gedreht! Zum einen standen Szenen für dotnet.tv 5 auf dem Plan, zum anderen mussten wir noch ein Interview mit Thomas Fickert für den ersten Community Clip drehen.

Community Clips berichten per Video von Events und anderen hervorhebenswerten Dingen, die sich in der Microsoft Entwicklergemeinde tun. Dabei geht es weniger um Technik, als vielmehr um die Menschen hinter der Technik. Dass der erste Community Clip mit der Community DevCon vom 13.2.2004 als Thema diesem Anspruch gerecht wird, zeigt ein Ausschnitt:

Natürlich konnten wir die Herausforderung zweier Videos nur annehmen, weil sie am gleichen Ort zu drehen waren:

Wir waren also in den Heiligen Hallen von Microsoft in Unterschleißheim - einem Ort der Rätsel und Verheißungen, einem Ort der Technik und der Menschen. Und insofern auch ein Ort mit quasi idealen Darstellern für die unterschiedlichsten Charaktere von dotnet.tv:



Es war toll, wie spontan und freundlich wir von Microsoft-Mitarbeitern in unterschiedlichen Abteilungen und Positionen unterstützt wurden. Die Arbeit ging dann auch recht zügig vonstatten, so dass wir abends wieder einmal in einem Hotel - dem Cortiina in München - drehen konnten. Dort galt es, das Thema Sicherheit in, ähm, im privaten Rahmen zu visualisieren:

Aber nicht alle Tagen können natürlich so entspannt ablaufen. Nach einem Tag Drehpause für den Schnitt des Community Clips musste wir heute eine ganz besondere Herausforderung meistern:

Für dotnet.tv scheuen wir halt keine Mühen: Kein Negerkuss ist uns zu süß, keine Maske zu aufwändig und keine Wand zu steil :-) Für das Thema Sicherheit geben wir einfach alles :-)
Kaum war die Folge 4 von dotnet.tv geschnitten, ging es auch schon an die Planung von Folge 5. Da Sebastian ab 10.3. für einen Monat im Ausland ist, müssen wir den Dreh der Folge 5 schon jetzt durchziehen - mit kaum 14 Tagen Abstand zum letzten.
Zuerst wollte ich gerade wegen der Kürze der Vorbereitungszeit dann ein Thema nehmen, in dem ich selbst drinstecke. Aber dann habe ich mit Michael Willers über Security gesprochen - und fand das Thema gar nicht mehr so schlimm :-) Im Gegenteil: hochinteressant. Und ein paar Ideen für Analogien zur Erklärung stellten sich auch schnell ein.
Eine war, Code Access Security Konzepte mit dem Registrierungsverfahren bei Entwicklerveranstaltungen zu vergleichen. Deshalb passte es ganz gut, dass der Technical Summit jetzt war.

So konnten wir einen Drehtag vorziehen.
Auf dem Event zwischen 500 Teilnehmern zu drehen, war allerdings eine besondere Erfahrung

Aber nicht nur für uns, sondern auch die Teilnehmer und den Veranstalter. An der einen oder anderen Stelle gab es denn auch verwunderte bis erschrockene Minen:

Aber ich bin guter Dinge, dass das Ergebnis alle zufriedenstellen wird. In jedem Fall hat es uns Spaß gemacht, dotnet.tv einmal in der Community zu drehen, für die wir es machen. Und auch die Teilnehmer schienen sehr interessiert an unserer Arbeit - und haben uns tatkräftig unterstützt.
Am Abend des ersten Veranstaltungstages haben wir dann erstmal Thomas Fickert von Microsoft (rechts im Bild), einem der Sponsoren von dotnet.tv, die Aufwartung gemacht.

Der Rahmen des Abendprogramms schien und richtig für eine gemütliche Plauderei über Gott und die Welt. Denn man kann ja nicht immer nur an dotnet.tv denken, oder? ;-)

Außerdem gab es so lecker zu essen, dass es am Ende mühsam war, die Portionen auch aufzuessen.

Nach erfolgreichem Feiern und noch einigen Szenen, die wir heute drehen mussten, haben wir dann die Heimfahrt dazu genutzt, bisheriges Material schon mal auf den Rechner zu kopieren und zu sichten. Die ICE Waggons mit Steckdosen machen es möglich:
Wasfürein Leben als Digitalnomade :-)
(Nur der Frau gegenüber an unserem Tisch war der Aufbau von zwei Rechnern, Kamera, externer Festplatte, Handys als GPRS-Modems und Kabelsalat nicht ganz geheuer :-)
Microsoft hat nach langer Zeit mal wieder einen "richtigen Entwickler-Event" gemacht: den Technical Summit in Kassel. Thema waren die Neuerungen, die mit dem .NET Framework 2.0 und VS.NET 2.0 (Codename Whidbey) kommen werden sowie ein Ausblick auf die nächste Windows Version (Codename Longhorn).
Mir hat die Veranstaltung gut gefallen. Sie hatte etwas sehr positiv Traditionelles.
Den bewährten Rahmen hat wieder das e-team organisiert, die Agenda bot 3 vollgepackte Tracks bis in den Abend hinein mit Night Sessions, die Sprecherriege rekrutierte sich aus Microsoft Developer Evangelists, Microsoft Regional Directors sowie anderen bekannten Größen aus dem In-/Ausland und die Abendbelustigung bot Alternativen von XBox Gaming Zone bis Chill Out bei Zigarre und Whiskey.
Der Veranstaltungsort in Kassel war gut gewählt. Es gab genügend Platz für die ca. 500 Teilnehmer.
Da ich selbst keinen Vortrag gehört habe, kann ich nichts dazu sagen, wie die Sessions gelaufen sind. Gehört habe ich jedoch von einer kurzweiligen Präsentation von Jackie Goldstein - einem Regional Director-Kollegen aus Israel -, der den Umgang mit Compuware-Produkten demonstriert hat. Dort wurden die Teilnehmer aktiv in den Vortrag mit einbezogen. Und es gab jede Menge Aha-Effekte, weil die Tools von Compuware bei den Zuschauern recht unbekannt waren. Für mich ein Zeichen dafür, dass Toolbesprechungen in den Fachmedien häufiger durchgeführt werden sollten.
Natürlich gab es auch wieder eine Ausstellung. Dort ist mir dieses Mal die Firma FastObjects aufgefallen. Sie stellen das gleichnamige objektorientierte Datenbanksystem her (früher POET) und bieten neuerdings ein Binding für den .NET Framework an. Man kann also aus C#/VB.NET usw. direkt auf das ODBMS zugreifen und Objekte persistieren. Eine Demo am Stand hat mich sehr beeindruckt - insbesondere, da mich die ObjektSpaces von Microsoft während der Vorbereitung auf meinen Technical Summit Vortrag enttäuscht haben.
Wieder daheim habe ich dann die FastObjects Trial-Version mal installiert. Und siehe da, innerhalb von 20min nach Einlegen der CD hatte ich die DB-Engine aufgespielt, ein Beispiel erfolgreich gestartet in die Doku geschaut und sogar eine erste kleine eigene ODBMS-Anwendung gebastelt. (Dafür war dann allerdings die Erinnerung an die Demo auf dem Tech. Summit wichtig, weil die Informationen zum Aufsetzen einer Anwendung in der Doku zu FastObjects doch recht weit verstreut ist. Da kann noch etwas nachgebessert werden.)
Diese Leichtigkeit der Installation eines kompletten, industrial strength ODBMS mit mehr als 10 Jahren Reife hat mich beeindruckt. Ich werde mich bestimmt näher damit beschäftigen.
Mein Eindruck von der Stimmung bei den Teilnehmern war gut. Die Vorträge scheinen gefallen zu haben, die Organisation hat funktioniert, der Abendevent war entspannend. Ich denke, es war eine sehr positive Veranstaltung - die hoffentlich nach der Pause im Jahr 2003 jetzt zu einer regelmäßigen Institution wird. Die Chance auf direkten Kontakt mit Microsoft selbst und Microsoft-nahen Fachleuten ist bei einer solchen Microsoft-Veranstaltung einfach am größten; und das hat durchaus Wert für die Teilnehmer als Anwender komplexer Produkte von Microsoft.
Zum Einstimmen hatte es am Montag noch einen PreCon-Tag mit einer Developer LAN Party mit Christian Weyer und mir - den .NET Twins :-) - als Leitern.
Acht Teilnehmer waren gekommen und wollten mit uns eine Anwendung entwickeln. Das Thema war wieder einmal vorher geheim :-) Geplant hatten wir aber die Realisierung einer Weblog Engine.
Nach einer gemeinsamen Planungsphase von 3-4 Stunden, die mehr oder weniger leserliche Dokumente produziert hat...

...ging es dann auch mit der Implementierung los. Während die aktive Beteiligung der Teilnehmer in der Planung (Analyse, Design) noch etwas verhalten war, waren bei der Realisierung alle mit Eifer dabei.


Mit Außnahme einer Unterbrechung am Abend für ein leckeres Pizzamahl haben fast alle bis 24h durchgehalten. Und als Ergebnis der Mühe war das Projekt soweit realisiert, dass man per Outlook Weblog-Einträge posten und in einem ASP.NET Frontend anschauen konnte. Ein Resultat, dass sich sehen lassen kann - auch wenn die Erwartungen am Anfang immer höher hängen. Denn das Entscheidende bei einer DevLanParty ist nicht die vollständige Umsetzung der Anforderungen, sondern das Lernen in der Gruppe. Und das hat stattgefunden, wie die Diskussionen der Teilnehmer untereinander bezeugten. Abstimmen, Schnittstellen definieren, jemandem Zuarbeiten, Vereinbarungen einhalten, realistisches Design usw. sind die Hauptlernziele der DevLanParty. Und dazu kommen dann auch noch Spaß und Wissen :-)
Bei der nächsten DevLanParty werden wir allerdings ein anderes Thema wählen. Es soll ja eine Überraschung sein.
ObjectSpaces needs three XML documents to completly define the mapping between persistent classes and SQL Server databases. However, keeping these mapping files separate from the application code has the drawback, that mapping info and code/class definitions can get out of sync. You need to remember to always deploy three additional files with your app's assemblies.
This problem can be solved by:
1. Defining all three mapping documents within just one XML file.
2. Embedding the one XML file into your assembly as a resource.
As a result you can focus on deploying just your assemblies and can be sure, your XML mappings are always correct.
How to integrate the OSD and RSD mappings into the MSD mapping info
In order integrate all mapping info into just one XML file you need to replace the <Schema Location="..."/> references in the MSD document with <InlineSchema> elements. Then put into these elements the XML definition from the OSD and RSD files.
MSD document referencing OSD/RSD mapping files:
<m:MappingSchema xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
<m:DataSources>
<m:DataSource Name="NorthwindRSD" Type="SQL Server" Direction="Source">
<m:Schema Location="rsd.xml"/>
<m:Variable Name="Customers" Select="Customers" />
</m:DataSource>
<m:DataSource Name="DataTypesOSD" Type="Object" Direction="Target">
<m:Schema Location="osd.xml"/>
</m:DataSource>
</m:DataSources>
<m:Mappings>
...
</m:Mappings>
</m:MappingSchema>
Single MSD document with OSD/RSD mapping info:
<m:MappingSchema xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
<m:DataSources>
<m:DataSource Name="NorthwindRSD" Type="SQL Server" Direction="Source">
<m:InlineSchema>
<rsd:Database Name="Northwind" Owner="sa"
xmlns:rsd="http://schemas.microsoft.com/data/2002/09/28/rsd">
<r:Schema Name="dbo"
xmlns:r="http://schemas.microsoft.com/data/2002/09/28/rsd">
...
</r:Schema>
</rsd:Database>
</m:InlineSchema>
<m:Variable Name="Customers" Select="Customers" />
</m:DataSource>
<m:DataSource Name="DataTypesOSD" Type="Object" Direction="Target">
<m:InlineSchema>
<osd:ExtendedObjectSchema Name="DataTypesOSD"
xmlns:osd="http://schemas.microsoft.com/data/2002/09/20/persistenceschema">
<osd:Classes>
...
</osd:Classes>
</osd:ExtendedObjectSchema>
</m:InlineSchema>
</m:DataSource>
</m:DataSources>
<m:Mappings>
...
</m:Mappings>
</m:MappingSchema>
How to embed a single MSD file into an assembly
Embedding the single MSD file, e.g. msdAll.xml, into an assembly is easy. Just call the Add Existing Item menu item on the project, add the file, and then set the Build Action file property to "Embedded Resource". When you compile the project, the file gets embedded into the assembly. Its name is the file name prefixed with the assemblies root namespace.
How to load the MSD document from an embedded resource
To load the MSD resource, you need to open a stream on the embedded file using GetManifestResourceStream() of its assembly. Don´t forget to fully qualify the resource name with its namespace!
Then you open a XmlTextReader on the resource stream.
However, due to an error in the current .NET Fx 2.0 version you can´t just write new XmlTextReader(myResourceStream), but have to pass in also a URL. Any URL/filename will do.
After that, you load the MSD mapping info into a MappingSchema instance which you pass to the ObjectSpace constructor.
That´s it.
Here´s an example:
Imports System.Data.SqlClient
Imports System.Data.ObjectSpaces
Imports System.Data.sqlxml
Imports System.Data.Mapping
Module Module1
Sub Main()
Dim conn As New SqlConnection("server=localhost;integrated security=true;database=northwind")
Dim s As IO.Stream
s = System.Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream("ConsoleApplication1.msdAll.xml")
Dim rd As New System.Xml.XmlTextReader("msdAll.xml", s)
Dim msd As New MappingSchema(rd)
Dim os As New ObjectSpace(msd, conn)
End Sub
End Module
Thanks to Jeff Reed from Microsoft for his help.
More Posts