# Monday, May 25, 2009
Linq to sql comes with a really nice GUI to create those .dbml-files (which then will be used to create code). This works well for SQL Server Databases. Anyway - Linq to sql may also be used to access Sql Compact Edition but you can't use the GUI to drag-drop tables from an SQL-CE-Database onto the dbml-Editor. There are a lot of articles on the web which use SqlMetal to create that dbml-file which then can be used inside Visual Studios GUI. Also there seemed to be not only few people that wanted SqlMetal to skip some tables - this would be very useful when you often change something in your DB-
Schema but not want to drop every unused table every time again in the GUI or maybe if you want to rename some columns afterwards... So - recently I was in this situation and it was absolutely clear that there will be more of that work... so what I was looking for was a way to script that dbml-generation with support to exclude and rename tables and more (since we often deal with prefixes at our tables which I dont want inside my LinqClasses). After some research I figured out a way that works pretty nice for me. I wont go too much into details here because its too much..
First of all I used ildasm to disassemble SqlMetal into IL-Code via dump. Then I replaced most of the private words with public and added public here and there later when needed. Then I re-assembled that code using ilasm with the /DLL option. Finally I had a nice DLL where all that stuff I needed was no longer internal - it was all public - so I went on and created an own console-project which uses the SqlMetal.dll (the filename really MUST be SqlMetal.dll). I took the important parts of the old main method using .NET-Reflector and finally I had the pieces seperated where the the dbml-structure was extracted from an .sdf-file and where this structure was used to generate the .dbml-file. Nice. This structure is very simple to modify and the changes are copied to the dbml without any problems except if you change a name of a type and not change the type-reference in the association objects. Finally I did not want to write special code into this project everytime I have to deal with a new .sdf-file I decided to use the luainterface project to embed the lua-scripting language (very often used in games) to use lua-code for callbacks such as table exclusion, table and relation renaming, column renaming and whatever comes away... all I now have to do when it comes to dbml creation is to start my console
tool with a little lua-script as an argument and this (might) register some hooks where I may react to some columns by returning a different name. quite simple isn't it?? Oh well - some might be wondering how this stuff works - since sqlMetal is strong named... well since I will use this tool only in development I simply skipped the Strongname verification on my machine for that assembly by reflecting its strong name and using sn.exe to add the skipping rule like that
sn.exe -Vr *,<strongname-key>
I don't know if this is possible with earlier versions of sn.exe - I am using VS2008 SP1 by now.
So in the end I named the prog SqlCerium - since it is SqlMetal specialized for SQL CE and Ce is the chemical symbol for Cerium which is also a metal :-)

posted on Monday, May 25, 2009 11:07:35 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Tuesday, February 03, 2009
A few days ago I was about to use the linq to SQL column property "Delay Loaded" for the first time. I wanted to return some plain data objects with an (propably larger) Image field left empty to load it later only if the entity would be selected in the GUI. So I used one Method to give me a set of objects with an empty image field and later another method to give me a specific image.
I already knew this Delay Loaded-Property would cause the creaton of a System.Data.Linq.Link<T> internally. So I thought as soon as I would do a read-access to that image-column the full content would be loaded from the database (though even write-accesses will imply a pre-read of that data so dont think about avoiding loading huge data by setting the field to another value).
I used my datacontext in a using block where I typically set the ObjectTrackingEnabled to false because I dont needed that if I only do readings to the DB. But in fact it turned out that this would also avoid loading fields that are "Delay Loaded" - so ObjectTrackingEnabled set back to true and everything worked fine as long as I initially accessed the image field inside the using-construct (ok that makes sense easily..).
I checked against the documentation where I found a short page about how to retrieve information as read-only and found out that setting OTE to false will also set DeferredLoadingEnabled to false and that would skip the expanding of one-to-one and one-to-many relations - BUT no word about Linq.Links :-)
Looking into the docs of DeferredLoadingEnabled also told me that if OTE is false DLE is not only set to false - its ignored completely - and again nothing about Delay-Loaded Fields. So maybe this info was helpful..

posted on Tuesday, February 03, 2009 3:54:21 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, October 07, 2008
Recently I've been evaluating usage of Entity Framework instead of Linq to SQL. My first steps took me into comparison of traversing object relations. I made up an example which will demostrate how my attempts went with linq first and then in contrast will do the same with entity framework...


so this is what the linq-Designer had created of the dummy-database... I then used this to enumerate all Persons and maybe their pets and their cars in a listBox...
 listBox.Items.Clear();
 using (var ctx = new EntitiesDataContext())
 {
     var dlo = new DataLoadOptions();
     dlo.LoadWith<Person>(p => p.Pets);
     dlo.LoadWith<Person>(p => p.Cars);
     ctx.LoadOptions = dlo;

     foreach (var person in ctx.Persons.OrderBy(k => k.Firstname))
     {
         listBox.Items.Add(String.Format("Person {0}, {1}", person.Lastname, person.Firstname));

         foreach (var pet in person.Pets)
             listBox.Items.Add(String.Format("  Has Pet {0} called {1}", pet.Species, pet.Petname));

         foreach (var car in person.Cars)
             listBox.Items.Add(String.Format("  Has a {0} {1} with sign {2}", car.Brand, car.Type, 
car.Sign)); } }
notice that this wont work the way its meant to be by using LoadWith with two 1..n relations to person because it might lead into very big and very redundant resultsets and linq wont allow it... so what linq does instead is to select Person and Car und then select every Pet when its enumerated. So this is the produced SQL-Trace:
SELECT [t0].[PersonId], [t0].[Firstname], [t0].[Lastname], [t1].[CarId], 
[t1].[PersonId] AS [PersonId2], [t1].[Brand], [t1].[Type], [t1].[Sign], ( SELECT COUNT(*) FROM [dbo].[Car] AS [t2] WHERE [t2].[PersonId] = [t0].[PersonId] ) AS [value] FROM [dbo].[Person] AS [t0] LEFT OUTER JOIN [dbo].[Car] AS [t1] ON [t1].[PersonId] = [t0].[PersonId] ORDER BY [t0].[Firstname], [t0].[PersonId], [t1].[CarId] exec sp_executesql N'SELECT [t0].[PetId], [t0].[PersonId], [t0].[Species], [t0].[Petname] FROM [dbo].[Pet] AS [t0] WHERE [t0].[PersonId] = @x1',N'@x1 uniqueidentifier',@x1='E069349B-9088-4136-A84D-B0A11E69EAD9' exec sp_executesql N'SELECT [t0].[PetId], [t0].[PersonId], [t0].[Species], [t0].[Petname] FROM [dbo].[Pet] AS [t0] WHERE [t0].[PersonId] = @x1',N'@x1 uniqueidentifier',@x1='971E0FC7-E1E7-4205-BEB6-F51FAF1560CF' exec sp_executesql N'SELECT [t0].[PetId], [t0].[PersonId], [t0].[Species], [t0].[Petname] FROM [dbo].[Pet] AS [t0] WHERE [t0].[PersonId] = @x1',N'@x1 uniqueidentifier',@x1='18986A72-C948-4C43-9498-D4060B721273'
I was used to work around this sometimes by iterating the first result, collecting the primary keys, then making one single select with a IN ( Id1, Id2, ... )-Clause to get all the related entries to insert the new relationships based on their foreign-Key from this select. But thats only for some performance considerations..

Now it September 2008 and we got the first Service Pack of Visual Studio 2k8 coming along with the first release of ADO.NET-Entity Framework... Long Story short... heres the same example with EF:

the desginer looka really similar to Linq-Designer (and currently to me it does not seem to provide that much more than linq by this release). But using these classes to traverse their relations is a little bit different to linq.. while linq will auto-expand their relations when they are enumerated - EF does not until you use the Load Method or you tell EF to Include (-Method) the relation (please correct me if I missed something at this point). Again Long Story short - the Code:
 listBox.Items.Clear();
 using (var ctx = new BlogDBEntities())
 {
     foreach (var person in ctx.Person.Include("Pet").Include("Car").OrderBy(k => k.Firstname))
     {
         listBox.Items.Add(String.Format("Person {0}, {1}", person.Lastname, person.Firstname));

         foreach (var pet in person.Pet)
             listBox.Items.Add(String.Format("  Has Pet {0} called {1}", pet.Species, pet.Petname));

         foreach (var car in person.Car)
             listBox.Items.Add(String.Format("  Has a {0} {1} with sign {2}", car.Brand, car.Type, 
car.Sign)); } }
Besides the easier-to-use Syntax of Include instead of the DataLoadOptions, by looking under the hood it turned out that EF does face the problem in a different way than linq here.. instead of avoiding a large resultset EF didn't seem to care a lot about that so the resulting Select-Statement was...
SELECT 
 [UnionAll1].[PersonId] AS [C1], 
 [UnionAll1].[Firstname1] AS [C2], 
 [UnionAll1].[Lastname] AS [C3], 
 [UnionAll1].[C2] AS [C4], 
 [UnionAll1].[C1] AS [C5], 
 [UnionAll1].[C3] AS [C6], 
 [UnionAll1].[PetId] AS [C7], 
 [UnionAll1].[Species] AS [C8], 
 [UnionAll1].[Petname] AS [C9], 
 [UnionAll1].[PersonId1] AS [C10], 
 [UnionAll1].[C4] AS [C11], 
 [UnionAll1].[C5] AS [C12], 
 [UnionAll1].[C6] AS [C13], 
 [UnionAll1].[C7] AS [C14], 
 [UnionAll1].[C8] AS [C15], 
 [UnionAll1].[C9] AS [C16]
 FROM  (SELECT 
     CASE WHEN ([Extent2].[PetId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
     [Extent1].[Firstname] AS [Firstname], 
     [Extent1].[PersonId] AS [PersonId], 
     [Extent1].[Firstname] AS [Firstname1], 
     [Extent1].[Lastname] AS [Lastname], 
     1 AS [C2], 
     CASE WHEN ([Extent2].[PetId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C3], 
     [Extent2].[PetId] AS [PetId], 
     [Extent2].[Species] AS [Species], 
     [Extent2].[Petname] AS [Petname], 
     [Extent2].[PersonId] AS [PersonId1], 
     CAST(NULL AS int) AS [C4], 
     CAST(NULL AS uniqueidentifier) AS [C5], 
     CAST(NULL AS varchar(1)) AS [C6], 
     CAST(NULL AS varchar(1)) AS [C7], 
     CAST(NULL AS varchar(1)) AS [C8], 
     CAST(NULL AS uniqueidentifier) AS [C9]
     FROM  [dbo].[Person] AS [Extent1]
     LEFT OUTER JOIN [dbo].[Pet] AS [Extent2] ON [Extent1].[PersonId] = [Extent2].[PersonId]
 UNION ALL
     SELECT 
     2 AS [C1], 
     [Extent3].[Firstname] AS [Firstname], 
     [Extent3].[PersonId] AS [PersonId], 
     [Extent3].[Firstname] AS [Firstname1], 
     [Extent3].[Lastname] AS [Lastname], 
     1 AS [C2], 
     CAST(NULL AS int) AS [C3], 
     CAST(NULL AS uniqueidentifier) AS [C4], 
     CAST(NULL AS varchar(1)) AS [C5], 
     CAST(NULL AS varchar(1)) AS [C6], 
     CAST(NULL AS uniqueidentifier) AS [C7], 
     1 AS [C8], 
     [Extent4].[CarId] AS [CarId], 
     [Extent4].[Brand] AS [Brand], 
     [Extent4].[Type] AS [Type], 
     [Extent4].[Sign] AS [Sign], 
     [Extent4].[PersonId] AS [PersonId1]
     FROM  [dbo].[Person] AS [Extent3]
     INNER JOIN [dbo].[Car] AS [Extent4] ON [Extent3].[PersonId] = [Extent4].[PersonId]) AS [UnionAll1]
 ORDER BY [UnionAll1].[Firstname] ASC, [UnionAll1].[PersonId] ASC, [UnionAll1].[C1] ASC
which of course returned a large resultset with many redundant entries. Since EF should know nothing about the actual data before selecting I dont assume that there might be any Statement-Generation-Changes as an optimization based on for example the amount of entries in the tables (which of course the database will do by its own). I've added the query plan here for your interest


I dont consider this as a problem for the database but it might be a problem if you'd fetch a larger resultset via network (besides the short-time client-/serversided increased memory consumption).

posted on Tuesday, October 07, 2008 1:00:20 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
-