Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Black Art – LINQ expressions reuse

, 9 Aug 2012
When developing a complex line of business system queries, reuse is often required. This article provides some guidelines and tools for LINQ expressions reuse.
LinqExpressionsReuse_v1.0.zip
LinqExpressionsReuse.suo
LinqExpressionProjection
bin
Debug
LinqExpressionProjection.dll
LinqExpressionProjection.pdb
Release
LinqExpressionProjection.csproj.vspscc
obj
Debug
DesignTimeResolveAssemblyReferencesInput.cache
LinqExpressionProjection.dll
LinqExpressionProjection.pdb
TempPE
Properties
LinqExpressionProjection.Test
bin
Debug
Dependencies
EntityFramework.dll
EntityFramework.dll
LinqExpressionProjection.dll
LinqExpressionProjection.pdb
LinqExpressionProjection.Test.dll
LinqExpressionProjection.Test.pdb
Release
Dependencies
EntityFramework.dll
LinqExpressionProjection.Test.csproj.vspscc
Model
obj
Debug
DesignTimeResolveAssemblyReferencesInput.cache
LinqExpressionProjection.Test.dll
LinqExpressionProjection.Test.pdb
ResolveAssemblyReference.cache
TempPE
Properties
LinqExpressionsReuse
bin
Debug
EF_ReuseLinq.exe
EF_ReuseLinq.pdb
EF_ReuseLinq.vshost.exe
EF_ReuseLinq.vshost.exe.manifest
EntityFramework.dll
LinqExpressionProjection.dll
LinqExpressionProjection.pdb
Dependencies
EntityFramework.dll
EF_ReuseLinq.csproj.user
Model
obj
x86
Debug
DesignTimeResolveAssemblyReferencesInput.cache
EF_ReuseLinq.exe
EF_ReuseLinq.pdb
ResolveAssemblyReference.cache
TempPE
Properties
TestResults
Asher_PC1600 2012-06-12 16_37_14.trx
Asher_PC1600 2012-06-12 16_37_14
Out
EntityFramework.dll
LinqExpressionProjection.dll
LinqExpressionProjection.pdb
linqexpressionprojection.test.dll
LinqExpressionProjection.Test.pdb
using System;
using System.Linq.Expressions;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using LinqExpressionProjection.Test.Model;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LinqExpressionProjection.Test
{
    public delegate bool MyDel(Project p);

    [TestClass]
    public class LinqExpressionsProjection_TestFixture
    {
        [TestMethod]
        public void Trials()
        {
            Func<Project, bool> p1 = p => p.Subprojects.Count() < 2;
            Expression<Func<Project, bool>> p2 = p => p.Subprojects.Count() < 2;
            //var p3 = (Project p) => p.Subprojects.Count() < 2;
            MyDel d = MyPredicate;
            Predicate<Project> pp = MyPredicate;
            using (var ctx = new ProjectsDbContext())
            {
                var v = ctx.
                    Projects.AsExpressionProjectable()
                    .Select(p => new
                                     {
                                         Proj = p,
                                         AA = _AASelector.Project<double>(),
                                         AA2 = p.Subprojects.Where(sp => sp.Area < 1000)
                                     .Average(sp => sp.Area)

                                     }).Where(o => o.AA < 300);
                var v2=v.ToArray();

            }
        }

        private Expression<Func<Project, double>> _AASelector =
            project =>
            project.Subprojects.Where(sp => sp.Area < 1000)
                .Average(sp => sp.Area);

        public bool MyPredicate(Project p)
        {
            return p.Subprojects.Count() > 2;
        }


        private static Expression<Func<Project, double>> _projectAverageEffectiveAreaSelectorStatic =
                proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);

        private Expression<Func<Project, double>> _projectAverageEffectiveAreaSelector =
        proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);

        public static Expression<Func<Project, double>> GetProjectAverageEffectiveAreaSelectorStatic()
        {
            return proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
        }

        public Expression<Func<Project, double>> GetProjectAverageEffectiveAreaSelector()
        {
            return proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
        }

        public Expression<Func<Project, double>> GetProjectAverageEffectiveAreaSelectorWithLogic(bool isOverThousandIncluded = false)
        {
            return isOverThousandIncluded
            ? (Expression<Func<Project, double>>)((Project proj) => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area))
            : (Project proj) => proj.Subprojects.Average(sp => sp.Area);
        }

        [TestMethod]
        [ExpectedException(typeof(NotSupportedException))]
        public void ProjectingExpressionFailsOnNormalCases_Test()
        {
            ValidateDb();
            Expression<Func<Project, double>> localSelector =
                proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
            using (var ctx = new ProjectsDbContext())
            {
                var v = (from p in ctx.Projects
                         select new
                         {
                             Project = p,
                             AEA = localSelector
                         }).ToArray();
            }
        }

        [TestMethod]
        [ExpectedException(typeof(NotSupportedException))]
        public void ProjectingExpressionFailsWithNoCallToAsExpressionProjectable_Test()
        {
            ValidateDb();
            Expression<Func<Project, double>> localSelector =
                proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects
                                select new
                                {
                                    Project = p,
                                    AEA = localSelector.Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ProjectingExpressionFailsWithProjectionNotMatchingLambdaReturnType_Test()
        {
            ValidateDb();
            Expression<Func<Project, double>> localSelector =
                proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = localSelector.Project<int>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ProjectingExpressionFailsWithWrongLambdaParameterType_Test()
        {
            ValidateDb();
            Expression<Func<Subproject, double>> localSelector =
                sp => sp.Area;
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = localSelector.Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        public void ProjectingExpressionByLocalVariable_Test()
        {
            ValidateDb();
            Expression<Func<Project, double>> localSelector =
                proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                           {
                                               Project = p,
                                               AEA = localSelector.Project<double>()
                                           }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        public void ProjectingExpressionByStaticField_Test()
        {
            ValidateDb();
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                           {
                                               Project = p,
                                               AEA = _projectAverageEffectiveAreaSelectorStatic.Project<double>()
                                           }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }


        [TestMethod]
        public void ProjectingExpressionByNonStaticField_Test()
        {
            ValidateDb();
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = _projectAverageEffectiveAreaSelector.Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        public void ProjectingExpressionByStaticMethod_Test()
        {
            ValidateDb();
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = GetProjectAverageEffectiveAreaSelectorStatic().Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        public void ProjectingExpressionByNonStaticMethod_Test()
        {
            ValidateDb();
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = GetProjectAverageEffectiveAreaSelector().Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        [TestMethod]
        public void ProjectingExpressionByNonStaticMethodWithLogic_Test()
        {
            ValidateDb();
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = GetProjectAverageEffectiveAreaSelectorWithLogic(false).Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(3600, projects[1].AEA);
            }
            using (var ctx = new ProjectsDbContext())
            {
                var projects = (from p in ctx.Projects.AsExpressionProjectable()
                                select new
                                {
                                    Project = p,
                                    AEA = GetProjectAverageEffectiveAreaSelectorWithLogic(true).Project<double>()
                                }).ToArray();
                Assert.AreEqual(150, projects[0].AEA);
                Assert.AreEqual(400, projects[1].AEA);
            }
        }

        private static void ValidateDb()
        {
            using (var ctx = new ProjectsDbContext())
            {
                try
                {
                    if (ctx.Projects.Count() != 2 || ctx.Subprojects.Count() != 5)
                    {
                        ClearDb();
                        PopulateDb();
                    }
                }
                catch (Exception)
                {
                    try
                    {
                        ClearDb();
                    }
                    catch (Exception)
                    {
                    }
                    PopulateDb();
                }
            }
        }

        private static void ClearDb()
        {
            using (var ctx = new ProjectsDbContext())
            {
                foreach (var subproject in ctx.Subprojects)
                {
                    ctx.Subprojects.Remove(subproject);
                }
                foreach (var project in ctx.Projects)
                {
                    ctx.Projects.Remove(project);
                }
                ctx.SaveChanges();
            }
        }

        private static void PopulateDb()
        {
            using (var ctx = new ProjectsDbContext())
            {
                Project p1 = ctx.Projects.Add(new Project());
                Project p2 = ctx.Projects.Add(new Project());

                ctx.Subprojects.Add(new Subproject() { Area = 100, Project = p1 });
                ctx.Subprojects.Add(new Subproject() { Area = 200, Project = p1 });
                ctx.Subprojects.Add(new Subproject() { Area = 350, Project = p2 });
                ctx.Subprojects.Add(new Subproject() { Area = 450, Project = p2 });
                ctx.Subprojects.Add(new Subproject() { Area = 10000, Project = p2 });

                ctx.SaveChanges();
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

Share

About the Author

Asher Barak
Software Developer Ziv systems, Israel
Israel Israel
Starting with Apple IIe BASICA, and working my way through Pascal, Power Builder, Visual basic (and the light office VBA) C, C++, I am now a C# .NET developer and designer (and a big fan of the .NET framework).
 
I am currently leading a major effort writing a new framework for SAP Business One extensions and overseeing the development of four products on the same (yet unfinished) framework

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 9 Aug 2012
Article Copyright 2012 by Asher Barak
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid