Archive for the ‘linq’ Category.

LINQ Expression Trees-Lambdas to CodeDom Conversion

Introduction

Some people are working to make the meta-programming possible. Some says as language oriented programming or domain specific language, but I prefer in general as meta-programming. For years programming languages supported to generate code with the powerful libraries or developers worked just with string concatenations and external linkers.

Nowadays meta-programming is getting more and more important as the domain expertise required. So the languages make meta programming possible at the compiler level with compiler directives.

Indeed there a lot of ideas coming from functional programming world where everything treated as expressions.  The code becomes data and data usage happens in the code. It should sound familiar with LINQ to SQL efforts to make this possible.

Libraries

.NET Framework had code generators since the beginning. CodeDom is probably the best known for tree based code generation. Codedom made possible to develop the ASP.NET engine, Windows Form designer, Web form designer, Web services wrapper LINQ entity objects and more. It is used extensively by the framework for the key technologies.

Although there are other APIs in .NET framework such as System.Reflection, System.Reflection.Emit, in this post we will focus on CodeDom and the new comer Expression Trees.

Expression Tree is the key API behind LINQ to SQL or IQueryable interface in general. Every query is expressed as typed trees that is parsed and converted to SQL later by the library.

The syntax of expressing queries is very readable with query comprehension syntax. However sometimes I want to know about the generated tree, like actually which functions are getting involved in the query. I have used Expression Tree Debugger Visualizer to draw the tree. It is pretty handy tool but for big trees it is difficult to see what is going on. This was my main motivation actually, although we had the code, we don’t see what’s the magic going on with query comprehension.

Implementation

So the idea is to have the code regenerated from the tree. In the real world this will involve a parser, interpreter and some more compiler theory which requires a lot of research. And because this is just for fun and since we have a powerful CodeDom library to generate code, I tried to convert the expression tree to CodeDom tree. Than used the CodeDom to generate code in any language. Finally I wrote the extension methods so that the debuggers and my code can use it directly from the type.

The compiler generates automatically the expression trees if we use the proper syntax. So from the beginning we have the tree. In order to convert to CodeDom objects, we need to traverse the tree and generate the necessary CodeDom objects. So I wrote a  tree walker that generates a CodeDom object to is parent while going to the last children. I didn’t realise how far it is going but that was it. When the tree walker finished with some more few lines of code the converter was just working.

I would like to put the code here as well but unfortunately it is too long for a blog post, so here are some snippets. Feel free to provide suggestions or bug reports.

LINQ Expression Visitor that generates CodeDom Trees

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
 
namespace ExpressionToCodedom
{
    public  class CodeDomExpressionVisitor
    {
 
        Expression m_exp;
        Dictionary<string, CodeTypeMember> m_members;
 
        public CodeDomExpressionVisitor(Expression e)
        {
            m_exp = e;
        }
        internal string GenerateSource(CodeDomProvider codeProvider)
        {
            StringBuilder sb = new StringBuilder();
            TextWriter tWriter = new IndentedTextWriter(new StringWriter(sb));
            CodeCompileUnit ccu = GenerateCode();
            codeProvider.GenerateCodeFromCompileUnit(ccu, tWriter, new CodeGeneratorOptions());
            codeProvider.Dispose();
 
            tWriter.Close();
 
            return sb.ToString();
        }
 
        internal string GenerateSource(string language)
        {
 
 
            CodeDomProvider codeProvider=null;
            if (language == "cs")
                codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
            else if (language == "vb")
                codeProvider = new Microsoft.VisualBasic.VBCodeProvider();
            else
            {                
 
                    throw new Exception("make sure you are trying to load a CodeDomProvider assembly");
 
            }
            return GenerateSource(codeProvider); 
 
        }
 
        public string GenerateSource()
        {
            return GenerateSource("cs"); 
        }
 
 
        private CodeCompileUnit GenerateCode()
        {
            var code = new CodeCompileUnit();
            m_members = new Dictionary<string, CodeTypeMember>();
 
            var LambdaTypeClass = new CodeTypeDeclaration("LambdaExpression");
            var ns = new CodeNamespace("Runtime");
 
            ns.Types.Add(LambdaTypeClass);            
            ns.Imports.Add(new CodeNamespaceImport("System"));
            // add more types in case I want to compile
 
            code.Namespaces.Add(ns);
 
            CodeObject cEvaluationResult = Visit(m_exp);
 
            var constructor = new CodeConstructor();
 
            if (cEvaluationResult is CodeStatement)
                constructor.Statements.Add(cEvaluationResult as CodeStatement);
 
            else if (cEvaluationResult is CodeExpression)
                constructor.Statements.Add(cEvaluationResult as CodeExpression);
 
            LambdaTypeClass.Members.Add(constructor);
 
 
            foreach (var item in m_members)
            {
                LambdaTypeClass.Members.Add(item.Value);
            }
 
            return code;
 
        }
 
        protected virtual CodeObject Visit(Expression exp)
        {
            if (exp == null)
                return null;
            switch (exp.NodeType)
            {
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.Not:
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.ArrayLength:
                case ExpressionType.Quote:
                case ExpressionType.TypeAs:
                    return this.VisitUnary((UnaryExpression)exp);
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                case ExpressionType.Coalesce:
                case ExpressionType.ArrayIndex:
                case ExpressionType.RightShift:
                case ExpressionType.LeftShift:
                case ExpressionType.ExclusiveOr:
                    return this.VisitBinary((BinaryExpression)exp);
                case ExpressionType.TypeIs:
                    return this.VisitTypeIs((TypeBinaryExpression)exp);
                case ExpressionType.Conditional:
                    return this.VisitConditional((ConditionalExpression)exp);
                case ExpressionType.Constant:
                    return this.VisitConstant((ConstantExpression)exp);
                case ExpressionType.Parameter:
                    return this.VisitParameter((ParameterExpression)exp);
                case ExpressionType.MemberAccess:
                    return this.VisitMemberAccess((MemberExpression)exp);
                case ExpressionType.Call:
                    return this.VisitMethodCall((MethodCallExpression)exp);
                case ExpressionType.Lambda:
                    return this.VisitLambda((LambdaExpression)exp);
                case ExpressionType.New:
                    return this.VisitNew((NewExpression)exp);
                case ExpressionType.NewArrayInit:
                case ExpressionType.NewArrayBounds:
                    return this.VisitNewArray((NewArrayExpression)exp);
                case ExpressionType.Invoke:
                    return this.VisitInvocation((InvocationExpression)exp);
                case ExpressionType.MemberInit:
                    return this.VisitMemberInit((MemberInitExpression)exp);
                case ExpressionType.ListInit:
                    return this.VisitListInit((ListInitExpression)exp);
                default:
                    throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType));
            }
        }
 
        protected virtual CodeObject VisitBinding(MemberBinding binding)
        {            
            switch (binding.BindingType)
            {
                case MemberBindingType.Assignment:
                    return this.VisitMemberAssignment((MemberAssignment)binding);
                case MemberBindingType.MemberBinding:
                    return this.VisitMemberMemberBinding((MemberMemberBinding)binding);
                case MemberBindingType.ListBinding:
                    return this.VisitMemberListBinding((MemberListBinding)binding);
                default:
                    throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType));
            }
        }
 
        protected virtual CodeExpression VisitElementInitializer(ElementInit initializer)
        {            
            ReadOnlyCollection<CodeExpression> arguments = this.VisitExpressionList(initializer.Arguments);
 
            return new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeThisReferenceExpression(),initializer.AddMethod.Name), arguments.ToArray());                               
        }
 
        protected virtual CodeObject VisitUnary(UnaryExpression u)
        {
            CodeObject operand = this.Visit(u.Operand);
 
            return operand;
        }
 
        private CodeBinaryOperatorType BindOperant(ExpressionType e)
        {
            switch (e)
            {
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return CodeBinaryOperatorType.Add;
 
                case ExpressionType.And:
                    return CodeBinaryOperatorType.BitwiseAnd;
 
                case ExpressionType.AndAlso:
                    return CodeBinaryOperatorType.BooleanAnd;                  
 
                case ExpressionType.Or:
                    return CodeBinaryOperatorType.BitwiseOr;                    
 
                case ExpressionType.OrElse:
                    return CodeBinaryOperatorType.BooleanOr;                    
 
                case ExpressionType.ExclusiveOr:
                case ExpressionType.ArrayIndex:
                case ExpressionType.Coalesce:
                case ExpressionType.RightShift:
                case ExpressionType.LeftShift:
                    throw new NotSupportedException("no direct equivalent in codedom,so workarounds not implemented");
 
                case ExpressionType.Equal:
                    return CodeBinaryOperatorType.IdentityEquality;
 
                case ExpressionType.NotEqual:
                    return CodeBinaryOperatorType.IdentityInequality;                    
 
                case ExpressionType.GreaterThan:
                    return CodeBinaryOperatorType.GreaterThan;                    
 
                case ExpressionType.GreaterThanOrEqual:
                    return CodeBinaryOperatorType.GreaterThanOrEqual;                    
 
                case ExpressionType.LessThan:
                    return CodeBinaryOperatorType.LessThan;                    
 
                case ExpressionType.LessThanOrEqual:
                    return CodeBinaryOperatorType.LessThanOrEqual;                    
 
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return CodeBinaryOperatorType.Multiply;
 
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return CodeBinaryOperatorType.Subtract;
 
                case ExpressionType.Power:
                case ExpressionType.Divide:
                    return CodeBinaryOperatorType.Divide;
 
                case ExpressionType.Modulo:
                    return CodeBinaryOperatorType.Modulus;
 
                default:
                    throw new Exception("are you sure you are right?");
            }
        }
 
        protected virtual CodeBinaryOperatorExpression VisitBinary(BinaryExpression b)
        {
            var left = this.Visit(b.Left) as CodeExpression;
            var right = this.Visit(b.Right) as CodeExpression;
            CodeObject conversion = this.Visit(b.Conversion);
 
            CodeBinaryOperatorType operant = BindOperant(b.NodeType);           
            var condExpr = new CodeBinaryOperatorExpression(left, operant, right);
            return condExpr;
        }
 
        protected virtual CodeObject VisitTypeIs(TypeBinaryExpression b)
        {            
            CodeObject expr = this.Visit(b.Expression);          
            return expr;
        }
 
        protected virtual CodeExpression VisitConstant(ConstantExpression c)
        {
            if (c.Value == null)
            {
                return new CodePrimitiveExpression(null);
            }
            else if (c.Value.GetType().IsValueType || c.Value.GetType() == typeof(string))
            {
                   return new CodePrimitiveExpression(c.Value);
            }
            else
            {
                return new CodeVariableReferenceExpression(c.Value.ToString());             
            }                        
        }
 
        protected virtual CodeObject VisitConditional(ConditionalExpression c)
        {            
            CodeObject test = this.Visit(c.Test);
            CodeExpression ifTrue = this.Visit(c.IfTrue) as CodeExpression;
            CodeExpression ifFalse = this.Visit(c.IfFalse) as CodeExpression;
 
            var ifStatement = new CodeConditionStatement(test as CodeExpression,
                                                         new CodeStatement[] {new CodeExpressionStatement(ifTrue) }, 
                                                         new CodeStatement[] {new CodeExpressionStatement(ifFalse) });                    
            return ifStatement;
        }
 
        protected virtual CodeObject VisitParameter(ParameterExpression p)
        {
            return new CodeArgumentReferenceExpression(p.Name);            
        }
 
        protected virtual CodeObject VisitMemberAccess(MemberExpression m)
        {
 
            CodeObject exp = this.Visit(m.Expression);
 
            if (exp is CodePrimitiveExpression)
            {
                return exp;
            }
            else
            {
                Type memType;
                if (m.Member.MemberType == MemberTypes.Field)
                    memType = (m.Member as FieldInfo).FieldType;
                else memType = (m.Member as PropertyInfo).PropertyType;
 
 
                m_members[m.Member.Name] = new CodeMemberField(memType, m.Member.Name);
                return new CodeVariableReferenceExpression(m.Member.Name);
            }
        }    
 
        protected virtual CodeObject VisitMethodCall(MethodCallExpression m)
        {           
            CodeObject obj = this.Visit(m.Object);
            IEnumerable<CodeExpression> args = this.VisitExpressionList(m.Arguments);
 
            if (obj == null)
            {  //static method call
                return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(m.Method.DeclaringType),m.Method.Name,args.ToArray());                
            }
            else
            {
                return new CodeMethodInvokeExpression(obj as CodeExpression, m.Method.Name, args.ToArray());
            }   
        }
 
        protected virtual ReadOnlyCollection<CodeExpression> VisitExpressionList(ReadOnlyCollection<Expression> original)
        {
            List<CodeExpression> list = new List<CodeExpression>();
            for (int i = 0, n = original.Count; i < n; i++)
            {
                CodeExpression p = (CodeExpression)this.Visit(original[i]);                
                    list.Add(p);                
            }            
            return list.AsReadOnly();
        }
 
        protected virtual CodeExpression VisitMemberAssignment(MemberAssignment assignment)
        {// thhose are properties
 
            CodeObject e = this.Visit(assignment.Expression);
            return e as CodeExpression;
 
        }
 
        protected virtual CodeObject VisitMemberMemberBinding(MemberMemberBinding binding)
        {
 
            IEnumerable<CodeExpression> bindings = this.VisitBindingList(binding.Bindings) as IEnumerable<CodeExpression>;
            return new CodeObjectCreateExpression(binding.Member.Name, bindings.ToArray());            
        }
 
        protected virtual CodeObject VisitMemberListBinding(MemberListBinding binding)
        {
 
            IEnumerable<CodeExpression> initializers = this.VisitElementInitializerList(binding.Initializers);
 
            return new CodeObjectCreateExpression(binding.Member.Name, initializers.ToArray());
 
        }
 
        protected virtual IEnumerable<CodeExpression> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
        {
            List<CodeExpression> list = new List<CodeExpression>();
            for (int i = 0, n = original.Count; i < n; i++)
            {
                CodeExpression b = this.VisitBinding(original[i]) as CodeExpression;
 
                    list.Add(b);
 
            }
            return list;
        }
 
        protected virtual IEnumerable<CodeExpression> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
        {
            List<CodeExpression> list = new List<CodeExpression>();
            for (int i = 0, n = original.Count; i < n; i++)
            {
                CodeExpression init = this.VisitElementInitializer(original[i]);
 
                list.Add(init);
 
            }
 
            return list;
        }
 
        protected CodeMethodReferenceExpression VisitLambda(LambdaExpression lambda)
        {
            var  body = this.Visit(lambda.Body);
            var lambdaMethod = new CodeMemberMethod();
 
            lambdaMethod.Name = lambda.Type.Name;
            if (lambdaMethod.Name.Contains("Func"))
                lambdaMethod.ReturnType = new CodeTypeReference(lambda.Body.Type);
 
            foreach (var item in lambda.Parameters)
            {
                lambdaMethod.Parameters.Add(new CodeParameterDeclarationExpression(item.Type, item.Name));
            }
 
            if (body is CodeExpression)
            {
                if (lambdaMethod.ReturnType.BaseType.Contains("Void"))
                    lambdaMethod.Statements.Add((body as CodeExpression ));
 
                else
                    lambdaMethod.Statements.Add(new CodeMethodReturnStatement(body as CodeExpression));
            }
            else if (body is CodeStatement)
            {
                    lambdaMethod.Statements.Add((body as CodeStatement));
            }
            else
            {
                throw new Exception("investigate...");
            }
 
            m_members[lambda.Type.FullName] = lambdaMethod;
            return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), lambdaMethod.Name) ;
        }
 
        protected virtual CodeObject VisitNew(NewExpression nex)
        {            
            IEnumerable<CodeExpression> args = this.VisitExpressionList(nex.Arguments);
 
 
            return new CodeObjectCreateExpression(nex.Type.Name,args.ToArray());
 
        }
 
        protected virtual CodeObject VisitMemberInit(MemberInitExpression init)
        {
            CodeObject n = this.VisitNew(init.NewExpression);
            CodeExpression[] bindings = this.VisitBindingList(init.Bindings).ToArray(); //binding will return property initialisation
 
 
            for (int i = 0; i < init.Bindings.Count; i++)            
            {
                                                                    // need to do something with that////
                var assignProperty = new CodeAssignStatement(new CodePropertyReferenceExpression(
                            n as CodeExpression, init.Bindings[i].Member.Name), bindings[i]);
            }                                   
 
            return n;
        }
 
        protected virtual CodeObject VisitListInit(ListInitExpression init)
        {
 
            CodeObject n = this.VisitNew(init.NewExpression);
            IEnumerable<CodeExpression> initializers = this.VisitElementInitializerList(init.Initializers);
 
            return n;
        }
 
        protected virtual CodeObject VisitNewArray(NewArrayExpression na)
        {
 
            IEnumerable<CodeExpression> exprs = this.VisitExpressionList(na.Expressions);
 
 
            return new CodeArrayCreateExpression(new CodeTypeReference(na.Type), exprs.ToArray());
        }
 
        protected virtual CodeObject VisitInvocation(InvocationExpression iv)
        {            
            IEnumerable<CodeExpression> args = this.VisitExpressionList(iv.Arguments);
 
            var expr = this.Visit(iv.Expression) as CodeExpression;
 
            return new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(expr, "Method"), args.ToArray());            
        }      
    }
}


Example

The extension methods enables to see the source code of any IQueryable and any Expression. Any of them have a GenerateSourceCodeMethod that gives back a string.

Expression Tree to CodeDom Visualizer

GenerateSourceCode(); // default C#

GenerateSourceCode(string language); // either cs or vb as input or  Fully qualified name of the CodeDomProvider (like Microsoft.FSharp.Compiler.CodeDom.FSharpCodeProvider) It should be added as a reference to the project if you’re going to use it.

Sample program that manipulates the expression trees and usage of CodeDom Converter with “item.GetCodeDomSource(“vb”)”

int a = 3, c = 2, d = 0;
 
var e1 = Expression.Constant(5);
var e2 = Expression.And(e1, e1);
Expression<Func<string, Func<bool>>> e3 = tbool => () => a < b && 8 > d || c == d;
Expression<Func<bool>> e4 = () => b < 4;
Expression<Func<RecordName, bool>> e5 = rn => rn.LastName == "ALFKI";
Expression<Func<StringBuilder>> e6 = () => new StringBuilder { Capacity = 20 };
Expression<Func<string, string>> e7 = word => word == "hello" ? "yes" : "no";
 
 
foreach (var item in new Expression[] { e1, e2, e3, e4, e5, e6,e7 })
{
    Console.WriteLine(item.GetCodeDomSource("vb"));
}

Visual Basic Output

Namespace Runtime
 
    Public Class LambdaExpression
 
        Private LastName As String
 
        Private Sub New()
            MyBase.New
            Me.Func`2
        End Sub
 
        Private Function Func`2(ByVal rn As Demo.Program.RecordName) As Boolean
            Return (LastName Is "ALFKI")
        End Function
    End Class
End Namespace

C# Output

namespace Runtime {
    using System;
 
 
    public class LambdaExpression {
 
        private string LastName;
 
        private LambdaExpression() {
            this.Func`2;
        }
 
        private bool Func`2(Demo.Program.RecordName rn) {
            return (LastName == "ALFKI");
        }
    }
}

Conclusion

Codedom is too much C# centric, so it’s hard to make it available for every language. The difference between Code Statement and Code Expressions sometimes makes it hard to convert from expression trees.

On the on the other hand Expression trees are too much LINQ oriented. They are less powerful than CodeDom but more easy to express. In expression trees everything is an expression unlike CodeDom. Some constructs are missing from expression trees like the assignment, but we will probably see the improvements in the expression trees in the future. So it might not be a true DSL or language generator, but sure it is enough to get the most of the databases.

There are some other more powerful meta-programming tools and libraries. F# quotation library supports all the available full-set language features expressed as quotations. Dynamic Language Runtime is another expression tree like library focussed more on compiler developers.

Finally this library is not build for runtime code conversion from expression tree to CodeDom, although it is possible. The CodeDom generated code is mainly for debugging to print the source code of the query. It might also be helpful for seeing what is going on under the hood.

RSS Feed Parser in 20 Lines with .NET LINQ

RSS is becoming more and more the face of web. I rarely visit the sites rather visit a bunch of them using an RSS reader. I was working with RSS data and realized how LINQ to XML made easier, elegant  and terse to manipulate XML data. Here is a very naive RSS  parser from RSS 2.0 specification

The Subscription and Channel classes are shown below. Normally I prototyped the parser with anonymous types but when using with methods I needed to get rid of the anonymous types and make them typed.

 
 
 
 
 public class Channel
        {
            public string Title { get; set; }
            public string Link { get; set; }
            public string Description { get; set; }
            public IEnumerable<Item> Items { get; set; }
        }
 public class Item
        {
            public string Title { get; set; }
            public string Link { get; set; }
            public string Description { get; set; }
            public string Guid { get; set; }
        }

Notice the usage of automatic properties as well to optimise readability and simplicity of the code.

Actually, here is where the F# power comes from, if I had implemented this in F#, I wouldn’t need to create those mock objects because of the type inference. C# inferenced types still needs to be more manually inferenced, like the subscription and channel class case.

In order to use we need to create an XDocument object that is preferably an RSS feed (that’s the purpose actually :) ) XDocument is an object that take the best features of XMLDocument and XMLReader. It doesn’t load the complete stream, it is possible to read forward and backward. It is very flexible, we can do transformation, parsing, writing, reading, querying etc.

The LINQ parser query is below :

static IEnumerable<Channel> getChannelQuery(XDocument xdoc)
        {
            return from channels in xdoc.Descendants("channel")
                        select new Channel
                        {
                            Title = channels.Element("title") != null ? channels.Element("title").Value : "",
                            Link = channels.Element("link") != null ? channels.Element("link").Value : "",
                            Description = channels.Element("description") != null ? channels.Element("description").Value : "",
                            Items = from items in channels.Descendants("item")
                                    select new Item
                                    {
                                        Title = items.Element("title") != null ? items.Element("title").Value : "",
                                        Link = items.Element("link") != null ? items.Element("link").Value : "",
                                        Description = items.Element("description") != null ? items.Element("description").Value : "",
                                        Guid = (items.Element("guid") != null ? items.Element("guid").Value : "")
                                    }};
        }

At the end there is no magic, it is like opening an XML reader and choosing the element and attribute we like. However LINQ to XML does this so nicely that I don’t wanna use XMLReader anymore.

It is needed to create the XDocument object and pass it to the function. Bare in mind the parsing is not executed until we loop for each element of the list (laziness).

 static void Main(string[] args)
        {
 
            string feedUri = "http://feeds.feedburner.com/canerten";             
            var myFeed = getChannelQuery(XDocument.Load(new StreamReader(HttpWebRequest.Create(feedUri).GetResponse().GetResponseStream())));
 
            foreach (var item in myFeed)
            {
                Console.WriteLine("{0} - {1}", item.Title, item.Description);
 
                foreach (var i in item.Items)
                {
                    Console.WriteLine("{0}", i.Title);
                }
            }
        }

Coding Day – Adventures in Computing – Can Erten’s Blog
Book Review-Expert F#
Distributed Functional Programming with F# MPI Tools
Power of Functional Programming, its Features and its Future

The code output is shown below. You can also get program.cs file below.

What I like about LINQ is its laziness and really gives a unified data model for all different sources. At the end everything is an IEnumerable no matter whether it is a database query or a memory object.

LINQ and XLINQ with Visual Basic Literals

I really liked the XML expressiveness of Visual Basic, let’s build a very simple MSN History Search Engine using LINQ and XML Literals in Visual Basic.

The best thing is those literals could be used in LINQ expressions. Remember the simple XML file that MSN stores as a history.

  • .@AttributeName : Accesses the attribute element in XML
  • .<ElementName> : Accesses the element in XML
  • …<Descendant name>: Accesses the descendant name in XML

Modifying the XML content is very neat as well either using the LINQ expressions or even in loops.

In .NET Framework 2.0 VB has one more feature called MY namespaces. It is very nice to access some dynamic data available like application or forms information. It also contains some helper functions to do some common tasks. Now I also found it very handy in a Windows Forms application.

Get the history files from the location and operate the XLINQ query:

Private Sub btnSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
 
For Each file In My.Computer.FileSystem.GetFiles(dirLocation)
ProcessFile(file)
Next
txtOutput.Text = sBuild.ToString()
 
End Sub

The XLINQ query that does search magic for the messages is as follows :

Function ProcessFile(ByVal s As String) As Boolean
If s.EndsWith("xml") Then
Dim msn = XElement.Load(s)
Dim q = From message In msn.<message> _
Where message.<text>.Value.Contains(txtSearch.Text) _
Select From = (message.<from>.@FriendlyName), Too = (message.<to>.@FriendlyNam), _
Message = message.<text>.Value
For Each msgFound In q
sBuild.AppendLine(msgFound.From + " says to " + msgFound.Too + _
" :  " + msgFound.Message)
Next
End If
End Function

What makes this different is the usage of literals. In C# that query would be longer than that.
In the sample message.<From>.@FriendlyName
means that it will get the From element and get the friendlyname attribute from it.

It is basically like having the XML data in your hands but there is no need to parse it or access the elements using the classes provided rather this work is done by the compiler at the compile time.

In a couple lines of code we have a fully featured MSN history searching. Let me know if you still want the source code (although that is all about it) or even the executable in case you are not into programming.

C# 3.0 vs. VB 9.0 and XML in the Language

Here is the summary of new language features as mentioned on the “What’s new on ORCAS

  • C# 3.0 Language Support: This CTP implements all of the C#3.0 language features from the May LINQ CTP including:
    • Query Expressions
    • Object and Collection Initializers
    • Extension Methods
    • Local Variable Type Inference and Anonymous Types
    • Lambdas bound to Delegates and Expression trees
    • Complete design-time support: Intellisense, Formatting, Colorization

  • VB 9.0 Language Support: This CTP contains the following language features:
    • Query Expressions: Basic querying, filtering, and ordering support
    • Object Initializers
    • Extension Methods
    • Local Variable Type Inference
    • Anonymous Types
    • XML literals
    • XML properties
    • New Line and Expression IntelliSense

LINQ is all in both of the languages and indeed this is the main feature for .NET Framework 3.5. Writing any type of queries is the purpose of LINQ at the end. Considering the abilities of LINQ, everything was possible before as well. LINQ makes us to get rid of the strings (the red coloured stuff) from the program in order to minimise the typo errors, easy to read programs by syntax highlighting. But all the best is that gives the ability to write declarative and functional style programs.

Beside the new language features, as a compiler improvement, it is very surprising that C# still doesn’t have background compilation. There is background syntax checking but no compilation. I believe this is a definite need for C# because it is really helpful. For instance the F# projects do background compilation and syntax checks, that way it easy to investigate “silly errors” while coding. Also this was one of the powerful features that I found on eclipse while working on a Java project.

Visual basic has that feature moreover it has also automatic syntax fixing as well. Likewise if you call a method with lower case letters it is automatically converted to the actual method on the next line. Actually in a type inferenced language this is needed, because it is not easy to recognise the type information of all the members.

Anyway I just wrote a quick macro to give the feeling of background compilation for C#. It is not really sophisticated but it works. Just put it into EnvironmentEvent macro in Visual Stuio.

Dim lastbuilt As DateTime
    Private Sub TextDocumentKeyPressEvents_AfterKeyPress(ByVal Keypress As String, ByVal Selection As EnvDTE.TextSelection, ByVal InStatementCompletion As Boolean) Handles TextDocumentKeyPressEvents.AfterKeyPress
        Dim doc = DTE.ActiveDocument
        Dim diff = DateTime.Now.Subtract(lastbuilt)
 
        If Not Char.IsLetterOrDigit(Keypress(0)) And diff.Seconds > 5 Then
            DTE.ExecuteCommand("Build.BuildSelection")
            lastbuilt = DateTime.Now
            doc.Activate()
        End If
    End Sub

XML in Language

All of the best is that now XML is a first class citizen in VB. I wouldn’t expect this as a serious feature but after trials it makes extremely relevant to use XML in Visual Basic. You get syntax highlighting and even intellisense for XML if the namespaces are specified and even more.

Having XML literals in the language, it makes really sense to use XLinq with VB.

Like consider the xml stored by messenger. You could just assign to a variable just like that.

        Dim msn = <?xml version="1.0"?>
                  <?xml-stylesheet type='text/xsl' href='MessageLog.xsl'?>
                  <Log FirstSessionID="1" LastSessionID="1">
                      <Message Date="25/03/2007" Time="22:35:47" DateTime="2007-03-25T21:35:47.173Z" SessionID="1">
                          <From><User FriendlyName="koko"/></From>
                          <To><User FriendlyName="opopop"/></To>
                          <Text Style="font-family:Comic Sans MS; font-weight:bold; color:#0000a0; ">151515</Text>
                      </Message>
                      <Message Date="25/03/2007" Time="22:35:55" DateTime="2007-03-25T21:35:55.344Z" SessionID="1">
                          <From><User FriendlyName="koko"/></From>
                          <To><User FriendlyName="opopop"/></To>
                          <Text Style="font-family:Comic Sans MS; font-weight:bold; color:#0000a0; ">5959959</Text></Message>
                  </Log>

It will have the type of System.Xml.Linq.XDocument.

Let’s define the XML Stylesheet :

        Dim xslt = <?xml version="1.0"?>
                   <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                       <xsl:template match="Log">
                           <html>
                               <head>
                                   <title> Message Log for </title>
                               </head>
                               <body style='margin:0'>
                                   <table border='1'>
                                       <tr>
                                           <td> From </td>
                                           <td> To </td>
                                           <td> Message </td>
                                       </tr>
                                       <xsl:for-each select="/Log/Message">
                                           <tr>
                                               <td><xsl:value-of select="From/User/@FriendlyName"/></td>
                                               <td><xsl:value-of select="To/User/@FriendlyName"/></td>
                                               <td><xsl:value-of select="Text"/></td>
                                           </tr>
                                       </xsl:for-each>
                                   </table>
                               </body>
                           </html>
                       </xsl:template>
                   </xsl:stylesheet>

If we want to do an XSLT transformation to that snippet, it is even easier than it used to be.

Dim xTransform = New System.Xml.Xsl.XslCompiledTransform()
xTransform.Load(xslt.CreateReader())
xTransform.Transform(msn.CreateReader(), New System.Xml.XmlTextWriter("test.html", New System.Text.UnicodeEncoding()))

I think working with xml data using Visual Basic should be considered as a manipulation language. Since we are all becoming multilingual this shouldn’t be a problem.