LINQ Adventures

   I know that every single person in my multi million user base is interested in LINQ. To your great satisfaction here are two pieces of somewhat strange LINQ behavior that you are (undoubtedly) going to enjoy.  The two pieces are completely unrelated. The first requires a little knowledge about LINQ to SQL and ADO.NET and the second requires good familiarity with the C# language (reading AND understanding the "What Is New in C# 3.0" series will do as well).


   LINQ to SQL Private Properties and the LinqDataSource


   The first odd behavior I would like to share is one of LINQ to SQL and the LinqDataSource. I was trying to add an option to the articles on this very no-blog (you can download the source code from "What is this place?" page) to limit the users who could post a comment. The three options are "Everyone", "Registered" and "Forbidden". I created a field of type TINYINT in the database to store what I called CommentsPolicy. However in the classes that I would work with I did not want the field to be mapped to byte (the normal mapping for TINYINT) but to enum that I created like this (note that I changed the enum's underlying type to match the database field):

   public enum CommentsPolicy : byte
   {
       Everyone = 0,
       Registered = 1,
       Forbidden = 2
   }

I created a partial class to add a property to the generated class and added the following property:

   public CommentsPolicy AllowComments
   {
       get
       {
           return (CommentsPolicy)this.CommentsPolicy;
       }
       set
       {
           this.CommentsPolicy = (byte)value;
       }
   }

   I also made the CommentsPolicy property private in order to preserve the encapsulation and this is where the problems started. I was posting articles, updating the comments policy and everything seemed to work fine until I tried to delete an article that had its comments policy set to "Forbidden". An exception was thrown informing me that the update check had failed. WTF?!? I did not even change the stupid property. Further testing showed that I am able to successfully delete any article from its own page but I can only delete articles that have comments policy set to "Everyone" from the front page. As it turns out the LinqDataSource I am using on the front page has problems with the update check for private properties. Obviously the default value for byte (0) is passed as a parameter for the update check so the update check only passes if the value in the database is also 0. I did not look further into the problem and I just set the update check to "Never" because I did not need it anyway and everything worked fine. However if you know something about this problem please post it in the comments. Who knows some day I may need this update check.

Update 13.12.2009

   I just found out how you can map an enum to an integer database column. All you need to do is edit the "Type" property of the column in the Linq to SQL designer and fill in the type of the enum. The trick here is that you need to use the global namespace alias "global::" when naming the type.


   C#'s Closures


   Some time ago we were looking for a way to write the equivalent of SQL "IN" in LINQ. My (very wrong) idea was to accumulate the values adding "where" to the query expression in a loop and then use De Morgan's laws to reverse the expression.

   Lets use a table called Mlasses with columns MID (INT, PK) and Malue (NVARCHAR(MAX)). Insert the following values:

   1 | a
   2 | b
   3 | c
   4 | d
   5 | e

   Lets make the equivalent of the following SQL query:

   SELECT * FROM Mlasses WHERE MID IN(3,4)

I intended to make it something like that:

       using (DataClassesDataContext dc = new DataClassesDataContext())
       {
           var query = from mlass in dc.Mlasses
                       where mlass.MID != 3 && mlass.MID != 4
                       select mlass;

           query = dc.Mlasses.Except(query);

           foreach (Mlass m in query)
           {
               Console.WriteLine(m.MID);
           }
       }

The output should be:

3
4

The query LINQ to SQL generated for my solution was not very pretty:

   SELECT DISTINCT [t0].[MID], [t0].[Malue]
   FROM [dbo].[Mlasses] AS [t0]
   WHERE NOT (EXISTS(
       SELECT NULL AS [EMPTY]
       FROM [dbo].[Mlasses] AS [t1]
       WHERE ([t0].[MID] = [t1].[MID]) AND ([t1].[MID] <> @p0) AND ([t1].[MID] <> @p1)
       ))

While according to De Morgan it is 100% equivalent it is definitely not good. However before making it right I stumbled on some much more interesting behavior. In the real world the number of values in the "where" clause was unknown. My solution should be rewritten as:

       using (DataClassesDataContext dc = new DataClassesDataContext())
       {
           int[] values = { 3, 4 };

           var query = from mlass in dc.Mlasses
                       select mlass;

           foreach (int i in values)
           {
               query = query.Where(mlass => mlass.MID != i); //dynamically adding where to the expression
           }

           query = dc.Mlasses.Except(query);

           foreach (Mlass m in query)
           {
               Console.WriteLine(m.MID);
           }
       }

   However the output was surprising:

   4

   The "3" is no more. To understand why we need to look at how closures work. In this case the closure captures the variable "i". The key word here is "variable". What is captured is exactly the variable and not the value. The variable is only one so combined with LINQ's deferred execution it has only one value – the last one. We can confirm that if we inspect the expression tree but that is not a simple task and will require a lot of screenshots so I will skip it. The closure captures the variable in the scope that it is declared. Unless the scope is closed the closure will capture the same variable. The scope of the variable "i" is the declaration of the foreach loop and not the scope of the body and this scope is not closed for all the iterations of the loop. To "fix" this we need to move the variable in a scope that is closed for each iteration and this is the body of the loop:

           foreach (int i in values)
           {
               int temp = i;
               query = query.Where(mlass => mlass.MID != temp);
           }

   This way we get our "3" back. I do not find the case where the lamba is compiled to an expression tree very interesting because it just adds ParameterExpression to the tree (did not check it though). However when the lambda is compiled to anonymous method everything seems much more complicated. Usually a variable captured in a closure is extracted as a field in an inner class of the class where the closure happens but when the number of fields is not known until runtime things become much more interesting. I wondered if in this case the compiler will create a List<int> field but it did not. To be honest I am not exactly sure about the following explanation because the IL code is not easy to follow but here is what I think the secret of the trick is:

   1. The compiler generates an inner class in the class that has the closure (mine was named "<>c__DisplayClass8" or "<>c__DisplayClass9". This class holds the anonymous method representing the lambda expression and the field for the captured variable.
   2. If the variable is captured from the outer scope (the declaration of the foreach) then an instance of the inner class is created before the actual foreach (i.e. the jump instructions) and the variable is attached to that instance's field and the SAME instance is passed to all the delegates.
   3. If the variable is captured in the inner scope (the body of the foreach) the compiler creates a new instance of the inner class inside the foreach (i.e. on every iteration), the variable is attached to the field of the current instance and these DIFFERENT instances are passed to the delegates.


   I hope all this makes sense to you.

   Oh, in case you were wondering this guy made LINQ to SQL generate an SQL "IN". He used the Contains method like this:

       using (DataClassesDataContext dc = new DataClassesDataContext())
       {
           int[] values = { 3, 4 };

           var query = from mlass in dc.Mlasses
                       where values.Contains(mlass.MID)
                       select mlass;

           foreach (Mlass m in query)
           {
               Console.WriteLine(m.MID);
           }
       }

Here is the generated query:

   SELECT [t0].[MID], [t0].[Malue]
   FROM [dbo].[Mlasses] AS [t0]
   WHERE [t0].[MID] IN (@p0, @p1)

Cool, isn't it?
Tags:   english programming 
Last edited by:   Stilgar
on   23:04 13.12.2009
Posted by:   Stilgar
09:06 22.06.2008

Comments:



No comments yet.




Post as:



Post a comment: