Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reusable linq select query in Entity Framework

In my application multiple reports are needed on some table, many of the fields are common in most reports, as a sample:

public class ReportStudent
{
    public int Id {get; set;}
    public string Name {get; set;}
    public string Family {get; set;}
    public DateTime BirthDate {get; set;}
    public DateTime RegisterDate {get; set;}
    public Double Average {get; set;}
    public string FatherName {get; set;}
    public string MotherName {get; set;}
}

var list1 = context.Students.Select(e=> new ReportStudent
{
    Id = e.Id
    Name = e.Name
    Family = e.Family
    BirthDate = e.BirthDate
    RegisterDate = e.RegisterDate
    FatherName = e.FatherName
    MotherName = e.MotherName
}).ToList();

var list2 = context.Students.Select(e=> new ReportStudent
{
    Id = e.Id
    Name = e.Name
    Family = e.Family
    BirthDate = e.BirthDate
    RegisterDate = e.RegisterDate
    Average = e.Average
}).ToList();

How can I write this map only once? These fields are common in list1 and list2.

Id = e.Id
Name = e.Name
Family = e.Family
BirthDate = e.BirthDate
RegisterDate = e.RegisterDate
like image 592
Mohammad Akbari Avatar asked Dec 04 '16 07:12

Mohammad Akbari


1 Answers

First, define an expression that will contain your common projection needs:

Expression<Func<ReportStudent, ReportStudent>> commonProjection = e => new ReportStudent
{
    Id = e.Id,
    Name = e.Name,
    Family = e.Family,
    BirthDate = e.BirthDate,
    RegisterDate = e.RegisterDate,
};

Then have a method that will modify this expression to reflect the additional bindings:

public static Expression<Func<ReportStudent, ReportStudent>> MergeBindings(Expression<Func<ReportStudent, ReportStudent>> expr, Expression<Func<ReportStudent, ReportStudent>> newExpr)
{
    var reportStudentType = typeof(ReportStudent);
    var eParameter = expr.Parameters.First();
    var eNew = Expression.New(reportStudentType);

    var memberInitExpr = expr.Body as MemberInitExpression;
    var memberInitNewExpr = newExpr.Body as MemberInitExpression;
    var allBindings = memberInitExpr.Bindings.Concat(memberInitNewExpr.Bindings.Select(x =>
        Expression.Bind(x.Member, Expression.Property(eParameter, x.Member as PropertyInfo)
    )));

    var eInit = Expression.MemberInit(eNew, allBindings);
    var lambda = Expression.Lambda<Func<ReportStudent, ReportStudent>>(eInit, eParameter);

    return lambda;
}

Usage:

var withParentsExpr = MergeBindings(commonProjection, e => new ReportStudent
{
    FatherName = e.FatherName,
    MotherName = e.MotherName
});

var list1 = context.Students.Select(withParentsExpr).ToList();

var withAverageExpr = MergeBindings(commonProjection, e => new ReportStudent
{
    Average = e.Average
});

var list2 = context.Students.Select(withAverageExpr).ToList();

(With some help from @Nicholas Butler great answer)

like image 93
haim770 Avatar answered Sep 25 '22 23:09

haim770