Introduction
Recently, I've been involved in integrating software into a third party's application. In order to integrate into their system, I had to create multiple XML files per Entity object.
Trying to do this by hand would be a very time consuming and futile exercise, given the number of Entity objects available. There must be a better way...
Background
I took a look around to see if I could find something that parses an EDMX file. About the only article I could find that was remotely related was here on CodeProject - "Generate Your Own Proxy for ADO.NET Data Services on Client Side" by Valentin Billotte.
I'm not much of a fan of XSL in this day and age for parsing a file. I don't care to cope with the maintainability and brittleness.
Since I really didn't find anything else, I decided to strike out on my own, and built a parser using LINQ to XML. It turned out OK, but I wasn't really happy with it. It looked something like this:
private static SortedList<string,> GetEntityModelTypes(XNamespace edmxNS,
IEnumerable<xelement> runtime, XNamespace schemaNS)
{
var list = (from e in runtime.Elements(edmxNS + "ConceptualModels")
.Elements(schemaNS + "Schema")
.Elements(schemaNS + "EntityType")
let properties = (from p in e.Elements(schemaNS + "Property")
select new EntityProperty
{
Member = GetValue(p.Attribute("Name")),
Nullable = GetValue(p.Attribute("Nullable"))
})
My first attempt worked, and would be easy to maintain, but if anything changed with the Entity Framework, the code I just got done writing would be obsolete before it got out the door.
Boy, it sure would be nice if Microsoft gave us a way of getting to the data in a meaningful way.
As it turns out, they did, but it isn't very intuitive. I stumbled upon the solution when I was looking for something else. The solution I found is buried in the code from the "Persistence Ignorance (POCO) Adapter for Entity Framework".
Once you know the secret of getting to the root data object, there really isn't much of a mystery. From that point on, it's just about traversing the data structures and grabbing the data that you are looking for. The project source code for this article will traverse the data structure for you and condense the data into new data structures for easier consumption.
The magical code to parse the EDMX file can be boiled down to the following three lines and referencing System.Data.Entity.dll.
public static EdmxEdmItemCollection ReadCsdlCollection(XElement edmx)
{
var csdlNodes = edmx.Descendants(EdmxNamespace +
ConceptualModels).First().Elements();
var readers = csdlNodes.Select(c => c.CreateReader());
return new EdmxEdmItemCollection(readers);
}
Requirements
- Visual Studio 2008 - For building the code
- T4 Toolbox - Needed to run the CodeGenerator\Driver.tt template
- Visual T4 plugin for Visual Studio (optional)
Using the Code
Since the magical three lines of code came from the Entity Framework POCO Adapter, I thought it would be appropriate to recreate their objects using my framework in conjunction with T4. This would be a quick and reasonable way to verify that my framework is working as intended. While I'm not going to discuss T4 or how to use it, you can get some additional information here:
In order to verify the objects I am creating with T4, I added a .bat file in the solution folder to run the Entity Framework POCO Adapter and produce files to compare against. By using a diff tool, I can be reasonably sure that I am parsing and producing correct output. Before running the .bat file, modify the paths to match your environment.
I should note that the global.cs file is the only file at the time of this writing that doesn't match exactly but is syntactically correct. The file is based on the notion of tables linked by a foreign key. As such, there is a relationship that can be expressed, starting with either table. The relationship is expressed by the framework from both points of view, but only one needs to be output for the global.cs file. The order that I am processing them in is not the same as the other framework; this accounts for the difference.
Running the Code
In order to create the objects, you will need to right click on the CodeGenerator\Driver.tt file and select "Run Custom Tool". This will create the items in the Output directory.
If you want to use your own EDMX file, you will need to open CodeGenerator\Driver.tt and change the constant: edmxFilename = @"Model\NorthwindEF.edmx
, as well as the path to the new file. When you save the Driver.tt file, the generator will kick in and re-generate the objects automatically.
Happy EDMX parsing...
History
2010-06-02
- Updated the template files. This change was made because of a breaking change in the T4 Toolbox. The T4 Toolbox change was made due to changes in Visual Studio 2010.
- Moved current code into a Version 1 folder.
- I created a new version for an upcoming project. The
EdmxLibrary
has been completely overhauled. I didn't add compatible T4 templates but a console app will display some of the data.