One of the new DLinq features in the May 2006 CTP is support for an External Mapping File. Included in the LINQ Preview is a document called, DLinq Overview for CSharp Developers, that includes a little overview of the feature.
Here is a snippet from the document:
- - - - - -
In addition to attribute-based mapping, DLinq now also supports external mapping. The most common form of external mapping is an XML file. Mapping files enable additional scenarios where separating mapping from code is desirable.
DataContext provides an additional constructor for supplying a MappingSource. One form of MappingSource is an XmlMappingSource that can be constructed from an XML mapping file. Here is an example of how mapping file can be used:
String path = @"C:\Mapping\NorthwindMapping.xml";
XmlMappingSource prodMapping =
XmlMappingSource.FromXml(File.ReadAllText(path));
Northwind db = new Northwind(
@"Server=.\SQLExpress;Database=c:\Northwind\Northwnd.mdf",
prodMapping
);
- - - - - -
Dlinq Example Using XML External Mapping File
O/R Mappers like NHibernate, WilsonORMapper, etc. have made the use of an external mapping file so commonplace that I have pretty much embraced the idea of using external mapping files as opposed to attributes.
To test the functionality, I created a simple Test Database that includes 2 tables, States and Cities, as well as a quick LINQ Windows Application that includes 2 classes, State and City:

The code for the City and State Classes are as follows:
namespace DlinqDemo
{
/// <summary>
/// State
/// </summary>
public class State
{
private int _id;
public int ID
{
get { return _id; }
set { _id = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private EntitySet<City> _cities =
new EntitySet<City>();
public ICollection<City> Cities
{
get { return _cities; }
set { this._cities.Assign(value); }
}
}
}
namespace DlinqDemo
{
/// <summary>
/// City
/// </summary>
public class City
{
private int _id;
public int ID
{
get { return _id; }
set { _id = value; }
}
private int _stateId;
public int StateId
{
get { return _stateId; }
set { _stateId = value; }
}
private EntityRef<State> _state;
public State State
{
get { return this._state.Entity; }
set { this._state.Entity = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
}
External XML Mapping File
The relationship between the classes and database tables are described in the following XML File ( Mappings.xml ), which I had to create by hand. Unless I am mistaken, SqlMetal.exe will not create an XML Mapping File for you.
<Database Name="test">
<Table Name="States">
<Type Name="DlinqDemo.State">
<Column Name="StateId" Member="ID"
Storage="_id"
DbType="Int NOT NULL IDENTITY"
IsIdentity="True"
IsAutoGen="True" />
<Column Name="Name" Member="Name"
Storage="_name"
DbType="NVarChar(100) NOT NULL" />
<Association Name="FK_Cities_States"
Member="Cities"
Storage="_cities" ThisKey="ID"
OtherTable="Cities"
OtherKey="StateId" IsParent="False" />
</Type>
</Table>
<Table Name="Cities">
<Type Name="DlinqDemo.City">
<Column Name="CityId" Member="ID"
Storage="_id"
DbType="Int NOT NULL IDENTITY"
IsIdentity="True"
IsAutoGen="True" />
<Column Name="StateId" Member="StateId"
Storage="_stateId"
DbType="int NOT NULL" />
<Association Name="FK_Cities_States"
Member="State"
Storage="_state" ThisKey="StateId"
OtherTable="States"
OtherKey="ID" IsParent="True" />
<Column Name="Name" Member="Name"
Storage="_name"
DbType="NVarChar(100) NOT NULL" />
</Type>
</Table>
</Database>
Strong Typing DataContext
Everything works through the DataContext Class. Per the documentation, it is much nicer to create a strongly typed version of it to keep things nice and neat:
namespace DlinqDemo
{
/// <summary>
/// Test
/// </summary>
public class Test : DataContext
{
public Table<State> States;
public Table<City> Cities;
public Test(string connection, MappingSource mapping)
: base(connection, mapping) { }
}
}
Test is an absolute horrible name. I just used the same name as the database, but a more descriptive and useful name would normally be in order.
Creating an Instance of the Test DataContext Class
I embedded the Mappings.xml file mentioned above into my windows application. The code to instantiate Test with the name of the connection string and XML Mapping File is:
XmlMappingSource mapping;
using (Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DlinqDemo.Mappings.xml"))
{
mapping = XmlMappingSource.FromStream(stream);
}
Test db = new Test("Data Source=.;Initial Catalog=test;Integrated Security=True", mapping);
You wouldn't hardcode your connection string like this, of course. If you don't want to embed your mapping file, you can pull it externally as shown in the code above from the documentation.
Querying
Now that one has an instance of the DataContext Class shown above, it is pretty easy to query for data:
** Get the state of Florida and city of Sarasota: **
State florida = db.States.Single(c => c.Name.Equals("Florida"));
City sarasota = db.Cities.Single(c => c.Name.Equals("Sarasota"));
** Get all the cities for Florida: **
ICollection<City> cities = florida.Cities;
** Append the number "2" to every city in Florida and save the changes back to the database: **
foreach (City city in cities)
{
city.Name += "2";
}
db.SubmitChanges();
** Create a new city and save it to the database: **
City tampa = new City();
tampa.Name = "Tampa";
tampa.State = florida;
db.Cities.Add(tampa);
// or florida.Cities.Add(tampa);
db.SubmitChanges();
** Add a new state and city to the database: **
State georgia = new State();
georgia.Name = "Georgia";
db.States.Add(georgia);
City atlanta = new City();
atlanta.Name = "Atlanta";
atlanta.State = georgia;
georgia.Cities.Add(atlanta);
db.States.Add(georgia);
db.SubmitChanges();
Conclusion
I am still coming up to speed on LINQ and DLinq myself, so there may be ways to improve the code above. If you know of any, please let me know :)
Resources:
Posted
Thu, May 18 2006 3:22 PM
by
David Hayden