7

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
1
  • One word: AutoMapper. Commented Dec 4, 2016 at 10:21

3 Answers 3

7

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)

Sign up to request clarification or add additional context in comments.

9 Comments

tnx for answer, but an error exist "type arguments for method 'Queryable.Select<TSource, TResult>(IQueryable<TSource>, Expression<Func<TSource, TResult>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly"
@MohammadAkbari, Where exactly does the error occur?
on select , context.Students.Select(withParentsExpr).ToList()
@MohammadAkbari, Did you change anything it the signature of MergeBindings()? What do you see when you hover on var withParentsExpr?
Expression<Func<ReportStudent, ReportStudent>>
|
0

If you don't want to write maps every time, you can use great library http://automapper.org/ With this library, you can define map and it automatically map all properties

Comments

0

You could create a function for that let say you have

public StudentReport ParseStudentReport(Student e)
{
    return new StutentReport{
         Id = e.Id
         Name = e.Name
         Family = e.Family
         BirthDate = e.BirthDate
         RegisterDate = e.RegisterDate
    }
 }

Then use it within your select statement

var list2 = context.Students.Select(ParseStudentReport);

Then add remaining properties or you could use AutoMapper, which can be found on github or as a nuget package.

2 Comments

how add remaining properties, plz give an example
This will not return an Expression that Entity Framework can translate into store query

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.