Click here to Skip to main content
15,886,796 members
Articles / Programming Languages / C#

Black Art – LINQ expressions reuse

Rate me:
Please Sign up or sign in to vote.
4.94/5 (16 votes)
2 Apr 2017CDDL8 min read 61.5K   312   39  
When developing a complex line of business system queries, reuse is often required. This article provides some guidelines and tools for LINQ expressions reuse.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using EF_ReuseLinq.Model;
using LinqExpressionProjection;

namespace EF_ReuseLinq
{
    public class Utilities
    {
        public static Expression<Func<Subproject, bool>> mainSubprojectSelector = sp =>
                                                                                   sp.Area ==
                                                                                   sp.Project.Subprojects.Where(spi => spi.Area < 1000)
                                                                                       .Max(spii => spii.Area);

        public static Expression<Func<Project, Subproject>> projectMainSubprojSelectorSingle =
            proj => proj.Subprojects.AsQueryable().Single(mainSubprojectSelector);

        public static Expression<Func<Project, Subproject>> projectMainSubprojSelectorSingleOrDefault =
            proj => proj.Subprojects.AsQueryable().SingleOrDefault(mainSubprojectSelector);

        public static Expression<Func<Project, Subproject>> projectMainSubprojSelectorFirst =
            proj => proj.Subprojects.AsQueryable().First(mainSubprojectSelector);

        public static Expression<Func<Project, Subproject>> projectMainSubprojSelectorFirstOrDefault =
            proj => proj.Subprojects.AsQueryable().FirstOrDefault(mainSubprojectSelector);

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

    class Program
    {
        // will not run - Single can only be the last query operator:

        // will not run - SingleOrDefault can only be the last query operator:


        // will not run - First can only be the last query operator:

        static void Main(string[] args)
        {
            // Run this once to poulate your DB:
            // PopulateDb();

            using (var ctx = new ProjectsDbContext())
            {
                // Commented lines will not run - reffere to article

                var mainsSbprojects = ctx.Subprojects
                    .Where(sp =>
                        sp.Area ==
                        sp.Project.Subprojects.Where(spi => spi.Area < 1000)
                        .Max(spii => spii.Area)).ToArray();

                var mainsSbprojectsBySelector = ctx.Subprojects
                    .Where(Utilities.mainSubprojectSelector).ToArray();

                var proj1MainSubProj = ctx.Subprojects
                    .Where(Utilities.mainSubprojectSelector).Single(sp => sp.Project.ID == 1);

                //var proj1MainSubprojByProj = ctx.Projects.Where(p => p.ID == 1).Select(projectMainSubprojSelectorSingle).Single();
                //var proj1MainSubprojByProj = ctx.Projects.Where(p => p.ID == 1).Select(projectMainSubprojSelectorFirst).Single();
                //var proj1MainSubprojByProj = ctx.Projects.Where(p => p.ID == 1).Select(projectMainSubprojSelectorSingleOrDefault).Single();
                var proj1MainSubprojByProj = ctx.Projects.Where(p => p.ID == 1).Select(Utilities.projectMainSubprojSelectorFirstOrDefault).Single();

                var projAndMainSubprojAreaBySubproj =
                    ctx.Projects.Where(p => p.ID == 1)
                    .Select(Utilities.projectMainSubprojSelectorFirstOrDefault)
                    .Select(sp => new { ProjectID = sp.Project.ID, MainSpArea = sp.Area })
                    .Single();


                var proj1Aea =
                    ctx.Projects.Where(p => p.ID == 1)
                    .Select(Utilities.projectAverageEffectiveAreaSelector)
                    .Single();

                var proj1AndAea =
                   ctx.Projects.AsExpressionProjectable()
                   .Where(p => p.ID == 1)
                   .Select(p => new
                            {
                                Project = p,
                                AEA = Utilities.projectAverageEffectiveAreaSelector.Project<double>()
                            }
                   ).Single();

            }
        }

        private static Expression<Func<Project, Subproject>> GetProjectMainSubprojectSelector()
        {
            return Utilities.projectMainSubprojSelectorFirstOrDefault;
        }


        private static Expression<Func<Project, Subproject>> SwitchSelector(bool retriveLargest)
        {
            return retriveLargest
                ? (Expression<Func<Project, Subproject>>)((Project p) => p.Subprojects.Where(x => x.Area == p.Subprojects.Max(y => y.Area)).FirstOrDefault())
                : (Expression<Func<Project, Subproject>>)((Project p) => p.Subprojects.Where(x => x.Area == p.Subprojects.Min(y => y.Area)).FirstOrDefault());
        }

        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();
            }
        }
    }

    class MyClass
    {
        public Subproject Subproject { get; set; }
    }
}

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)


Written By
Chief Technology Officer 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 full stack developer and development manager. Mostly with MS technologies on the server side and javascript(typescript) frameworks on the client side.

Comments and Discussions