User login |
C#Serializing Entity Framework ThingsThere are two flavors of serialization you can use on Framework entities: good ol' XML serialization and the DataContract serialization. XML serialization is pretty straightforward - it serializes the object it's given. But it does not walk references (I'm talking foreign keys here) or collections. whereas the DataContract serializer does handle the references and keeps track of what objects have been serialized and what hasn't. If you're going to use XSLT on the serialization, I'm not sure at this point which one is better - they're probably equal but time will tell. Unfortunately you can't directly serialize the entire ObjectContext, only collections or individual objects can be serialized. But this isn't as bad as it sounds as usually you'll want to start at some 'master object' in a collection and serialize it and all it references, so the inability to directly serialize the Context is not a big loss. So how do you do it? Assume we have a Context named RecipeModel which holds a bunch of recipes and related items (ingredients, techniques, etc.) and we want to serialize a specific recipe and all the ingredient info and technique info that it references. First find the Recipe:
Then setup the serializer:
using ( FileStream fs = File.OpenWrite( "RecipeData.xml" ) ) {
XmlWriter writer = XmlWriter.Create( f2 );
XmlSerializer srlz = new XmlSerializer( typeof( Recipe ) );
srlz.Serialize( writer, recipe );
writer.Close(); // ensure it's properly flushed
}
That's it. Recipe and all its data are now in the file. But the foreign key references to things like ingredients and techniques aren't there. If you want to include them you'll need to provide a wrapper which references them directly. Ugly if you have a lot of different scenarios you need to address. But for one or two it may not be too much work. On the other hand, you can use the DataContractSerializer - it handles the foreign key stuff nearly magically. Try this:
using ( FileStream fs = File.OpenWrite( "RecipeData.xml" ) ) {
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter( fs, Encoding.UTF8 );
DataContractSerializer srlz = new DataContractSerializer( typeof( Recipe ) );
srlz.Serialize( writer, recipe );
writer.Close(); // ensure it's properly flushed
}
That's it. The Recipe and all data it references via keys are collected into a single serialization. But of course there's a caveat or two. What if the referenced objects/collections aren't loaded? Does this somehow load them prior to serialization? Nope - if the referenced objects aren't loaded then all you'll get serialized is the keys that refer to them. If you want the data then make sure it's loaded. recipe.Ingredients.Load(); recipe.Techniques.Load(); Or some such. Of course there is a better way - using a LinQ query to eager load the references (and isn't there normally a query involved in all this anyways?):
srlz.Serialize( writer,
( from rcp in model.Recipes.Include( "Ingredients" ).Include( "Techniques" )
where rcp.Name = "Bordelaise Sauce"
select rcp ).First()
);
Two things: using the "Include()" forces eager loading of the entire referenced graph (whih in this case is probably what's needed) and second, notice the First() method on the query - this forces the query to execute and returns a single object rather than the query object itself (you can't serialize an ObjectQuery). Obvioulsy if you're needing to deserialize back to objects, the DataContract method is the way to go but if you're needing to translate your database data to something else (which is what I need to do) then XML/XSLT is worth a look and the above serializers will get half the job done with virtually no pain. XSLT on the other hand.....
ObjectListView & Dynamic Columns,generic method,attributesObjectListView has got to be one of the slickest controls out there - simple, quick and powerful. But being the extremely lazy coder that I am, it wasn't easy enough. So here's a little helper which allows you to decorate a class' properties with an attribute which is used to build the column definitions for the ObjectListView. Define your properties, adorn with an attribute and hand a collection to the listView - the rest is pretty much magic. I won't explain more here, the documentation has examples: here. Follow the Class List to OLVColumnAttribute and scroll down to the examples. Download here.
With Distinction
I wanted to fire an event when a particular entity type is updated in the store. For example, when any value in a table is modified. How to do it?
The context's SavingChanges event is the entry but how to determine which type of data is getting updated? Enter the ObjectStateManager. It'll return collections based on object states, modified, new, deleted, etc. All we need to do is examine the modified objects and fire events based on their types, right?
Not so fast - the state manager returns ALL the objects being saved, so there could be many, many entities of any given type and simply firing events when we see a prticular type would result in many unnecessary events.
Enter a Distinct Linq query:
var entSets = ( from obj in ObjectStateManager.GetObjectStateEntries( EntityState.Modified )
select obj.EntitySet ).Distinct();
This query will gather the EntitySet references from all the objects being saved.
Notice the oarens around the query and the Distinct() tacked on the end - this will filter the set to eliminate duplicates - so when we iterate the set, we get exactly one instance of each affected EntitySet.
Then we match the EntitySet to an event and we're done.
abbreviated...
foreach ( var set in entSets.Distinct() ) {
if ( set.Name == "SysVars" )
log.Info( "SysVars Modified" );
}
Deferred Identities
So how does one use identity keys (a.k.a AutoIncrement IDs) with Entities? It's simple. Really. Just not quite what I expected.
Assume this table:
CREATE TABLE svar ( id INTEGER PRIMARY KEY NOT NULL, name NVARCHAR(32) DEFAULT NULL, );Simple. Inserting a record should increment the id field ensuring that it's always unique. But the Entities thing has no provisions for this - or does it? It's not in the GUI (yet?), you have to edit the XML (.edmx) directly, adding an attribute to the column definition:
<EntityType Name="svar">
<Key>
<PropertyRef Name="id" />
</Key>
<Property Name="id" Type="integer" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="name" Type="nvarchar" MaxLength="32" />
LinQ and Forms
Iterating a Form's controls:
var qry = from c in this.Controls.OfType<Control>()
select c;
foreach ( var c in qry )
MessageBox.Show( c.Name + ":" + c.GetType().ToString(), "Control Type" );
The Controls collection is not directly usable in the query - the OfType method performs the necessary conversions.
To grab all controls of a specific type:
var q2 = from c in this.Controls.OfType<NumericUpDown>() select c;
Lambda & Forms
Lambda expressions and statements:
In the context of Forms programming, is there an advantage over anonymous methods? The lambda form is certainly more succinct and (with a bit of practice) simpler to read.
Examples:
Some simple callbacks as expressions:
// pop a message button.Click += ( sender, args ) => MessageBox.Show( "Click" ); // increment a numeric updn button.Click += ( sender, args ) => number.Value = number.Value + 1;Here's the same things as anonymous methods:
button.Click += delegate( object sender, EventArgs e ) { MessageBox.Show( "Click" ); }
button.Click += delegate( object sender, EventArgs e ) { number.Value = number.Value + 1; }
Slightly more complex callback as a lambda statement (note the enclosing {}):
this.FormClosing += ( sender, args ) => {
if ( MessageBox.Show( "Really?", "Quit?", MessageBoxButtons.OKCancel ) != DialogResult.OK )
args.Cancel = true;
};
And as an anonymous method:
button.Click += delegate( object sender, FormClosingEventArgs args ) {
if ( MessageBox.Show( "Really?", "Quit?", MessageBoxButtons.OKCancel ) != DialogResult.OK )
args.Cancel = true;
};
In this context (simple examples) there's very little difference (especially since they apparently compile to nearly identical code) other than brevity.
One thing I've found is that my event handlers and EventArgs derivatives tend to have really long names - the brevity of the lambda syntax is very appealing from that standpoint.
Inheritance HierarchiesBlaahhhhhhh......... Here MS made this really nice system, with a pretty well thought out object hierarchy. But can they make everything that goes on a Form share a common ancestor? Can they? No. While all Controls have a Name property and all ToolStrip items have a Name property, is there some common ancestor that also provides the Name property? No. Of course not. So if you want to treat all objects in a Form equally, you have to use reflection for certain properties which is just silly. They should have moved the common core properties to a common ancestor.
SerializationSo after stumbling around trying to load objects from an XML file, I see the Serialization package - waaayy cool. It's almost point-and-shoot simple. I get the serialization working for the project - all very cool - and all of a sudden it stops. Stops cold. Won't load, won't unload. No clues, just a cryptic message that "Can't serialize object". Hmmmm.... after considerable time trying to figure out what the hell I'd changed, I stumpled on it.
|