Thursday, February 11, 2010

Now Change the LINQ Statement to Dynamic Lambda

But what if you don’t actually know at design time which properties need to be copied over or what the where clause should look like? In this case, you need to dynamically build a Lambda Expression to replace the LINQ statement.
  • You will need to use the Parameter factory method from System.Linq.Expressions.Expression to create two System.Linq.Expressions.ParameterExpression variables – one for Person and one for FileFormat.
  • You will need to use the New factory method from System.Linq.Expressions.Expression to create a System.Linq.Expressions.NewExpression variable for FileFormat with a value of “new FileFormat()”.
  • Create a generic list of System.Linq.Expressions.MemberBinding to hold all of the property bindings that will be used. This is the “PersonID = p.PersonID” portion of the statement. I recommend putting this into a separate method.
    • Use the GetMember method to create a System.Reflection.MemberInfo variable for the Property.
    • Use the PropertyOrField method of System.Linq.Expressions.LambdaExpression to create a MemberExpression variable.
    • Use the Bind method of System.Linq.Expressions.Expression to return the MemberBinding.
  • Use the MemberInit method of System.Linq.Expressions.Expression to create a MemberInitExpression variable.
  • Use the Lambda method of System.Linq.Expressions.BinaryExpression to create the Lambda expression for the select.
LISTING 1.3
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
 
namespace DynamicLINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            IQueryable<Person> persons = new List<Person>{
                new Person{PersonID=1, CompanyID=1, FirstName="John", LastName="Smith"},
                new Person{PersonID=2, CompanyID=1, FirstName="Sam", LastName="Jones"},
                new Person{PersonID=3, CompanyID=1, FirstName="Jean", LastName="Smith"},
                new Person{PersonID=4, CompanyID=2, FirstName="Samantha", LastName="Maples"},
                new Person{PersonID=5, CompanyID=1, FirstName="Tom", LastName="Brown"}
            }.AsQueryable();
 
            ParameterExpression ffParam = Expression.Parameter(typeof(FileFormat), "f");
            ParameterExpression pParam = Expression.Parameter(typeof(Person), "p");
 
            Expression right = Expression.Constant(1);
            MemberExpression left = LambdaExpression.PropertyOrField(pParam, "CompanyID");
            BinaryExpression compareString = Expression.Equal(left, right);
            Expression<Func<Person, bool>> whereExp = (Expression<Func<Person, bool>>)
              BinaryExpression.Lambda(compareString, new ParameterExpression[] { pParam });
 
            NewExpression newExp = Expression.New(typeof(FileFormat));
 
            List<MemberBinding> bindings = new List<MemberBinding>();
            bindings.Add(GetMemberBinding("PersonID", pParam, "PersonID"));
            bindings.Add(GetMemberBinding("FirstName", pParam, "FirstName"));
            bindings.Add(GetMemberBinding("LastName", pParam, "LastName"));
 
            MemberInitExpression memberInitExpression =

System.Linq.Expressions.Expression.MemberInit(newExp, bindings);

 
            Expression<Func<Person, FileFormat>> selector =

(Expression<Func<Person, FileFormat>>)BinaryExpression.Lambda(memberInitExpression, pParam);

 
            List<FileFormat> fileFormat = persons.Where(whereExp).Select(selector).ToList();
 
            Console.WriteLine(fileFormat.Count.ToString());
            Console.ReadLine();
        }
        static MemberBinding GetMemberBinding(string property, ParameterExpression param, string column)
        {
            MemberInfo memberInfo = typeof(FileFormat).GetMember(property)[0];
            MemberExpression memberExpression = LambdaExpression.PropertyOrField(param, column);
            return System.Linq.Expressions.Expression.Bind(memberInfo, memberExpression);
        }
    }
    public class Person
    {
        public int PersonID { get; set; }
        public int CompanyID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    public class FileFormat
    {
        public int PersonID { get; set; }
        public int CompanyID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int CoverageID { get; set; }
        public int ProductID { get; set; }
        public double CoverageAmount { get; set; }
        public double CoverageCost { get; set; }
        public DateTime CoverageDate { get; set; }
    }
 
}


As evident in the code in Listing 1.3, decisions can be made at run time to determine what should be in the where and the select clauses of the query. The final value of whereExp is p => (p.CompanyID = 1). The final value of selector is p => new FileFormat() {PersonID=p.PersonID, FirstName=p.Firstname, LastName=p.LastName}

Wednesday, February 10, 2010

Build an Object from Other Object(s)

If there are no unknowns at design time, you can simply build a LINQ command that will accomplish this. In this example I have two classes – Person and FileFormat. I fill an IQueryable list of type Person and then use that list to populate an Generic list of type FileFormat. I then count the records that are copied to the new list.

LISTING 1.2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
 
namespace DynamicLINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            IQueryable<Person> persons = new List<Person>{
                new Person{PersonID=1, CompanyID=1, FirstName="John", LastName="Smith"},
                new Person{PersonID=2, CompanyID=1, FirstName="Sam", LastName="Jones"},
                new Person{PersonID=3, CompanyID=1, FirstName="Jean", LastName="Smith"},
                new Person{PersonID=4, CompanyID=2, FirstName="Samantha", LastName="Maples"},
                new Person{PersonID=5, CompanyID=1, FirstName="Tom", LastName="Brown"}
            }.AsQueryable();
 
            List<FileFormat> fileFormat =
               (from p in persons
               where p.CompanyID == 1
select new FileFormat
              {
                    PersonID = p.PersonID,
                    CompanyID = p.CompanyID,
                    FirstName = p.FirstName,
                    LastName = p.LastName
              }).ToList();
 
            Console.WriteLine(fileFormat.Count.ToString());
            Console.ReadLine();
        }
    }
    public class Person
    {
        public int PersonID { get; set; }
        public int CompanyID { get; set; }
        public string FirstName { get;set; }
        public string LastName { get;set; }
    }
    public class FileFormat
    {
        public int PersonID { get; set; }
        public int CompanyID { get; set; }
        public string FirstName { get;set; }
        public string LastName { get;set; }
        public int CoverageID { get;set; }
        public int ProductID { get; set; }
        public double CoverageAmount { get;set; }
        public double CoverageCost { get;set; }
        public DateTime CoverageDate { get;set; }


The output from Listing 1.2 is 4 since PersonID 4 is eliminated by the LINQ query.

Tuesday, February 9, 2010

Left Outer Joins in LINQ

To create a left join, you will need to add an into clause after the join statement, add an additional from clause in which you invoke the DefaultIfEmpty method. In my example below, I used the same alias in the second from clause as I used in the join. I did this because the second from clause represents the same data as the join clause. Also, after you put the into clause in the query, the alias from the join is no longer in scope. In the select clause, you will need to handle any nulls that may occur.

LISTING 1.1


var baffMapping =
     from ff in ccFileFormat
     join col in jobColumnMapping on ff.ccFileFormatID equals col.FileFormatID into x
     from col in x.DefaultIfEmpty()
     select new
     {
          PropertyName = ff.PropertyName,
          ColumnName = col == null ? string.Empty : col.ColumnName
     };


Current Project - LINQ and Dynamic Lambda

I have been working lately to redesign a system that is currently very database centric. It is an EDI system that produces both input and output files. For inbound files, we take in various file formats and map them to our business data and either insert or update our data. What we insert/update depends entirely on how our clients implemented their case. The same goes for outbound files. Each case is different.
The system was originally implemented with all the system logic in SQL Server stored procedures. We also archive all of the clients data every time we produce a file. Each new file means a new stored procedure and a new table to hold the archive data. The archive data is used to do change analysis. Many of our clients only want to see data that has changed in their files.
After several years, the system is extremely large and growing much too fast. What we need is a more flexible system where the business logic is in .Net code. We want to store only data that has changed since the last time a file was produced.
Here is what I'm thinking...
We need a database that contains metadata about our data. It will also contain information like how the data relates together. Our clients can then go in and configure what data that they want to see in the files.
We can then use LINQ and dynamic Lambda expressions to put the clients configuration together. I have not found many resources on how to build the dynamic Lambda expressions. I will be sharing some of the things I've discovered.

Friday, August 21, 2009

I am opening this blog as a place to start gathering thoughts for an upcoming book. I am strongly opinionated on application development subjects. I plan to discuss the complexity of applications and if programmers are over complicating the applications. I plan to discuss the relationships that IT professionals must have with their business partners.