Using LINQ Expression Trees to build maintainable DTO modeling
Tags: dotnet, csharp
Published: 2021-07-02 22:00:36

Using LINQ Expression Trees to build maintainable DTO modeling
One of the biggest problems I faced working on a medium sized .NET Framework code base was writing maintainable modeling systems without excessive code duplication.
It becomes all too easy to utilize extension methods to model your DTOs from a given query's select, or to pull out too much data from your data set in order to instantiate a DTO. With expression tress you can minimize the data pulls as well as create simple mappings of the data.

---

Let's say we have two classes in our dataset: *Student* and *Class*.

```
public class Student
{
	public int Id { get; set; }
	public string Name { set; set; }
	public virtual ICollection<Class> Classes { get; set; }
}

public class Class
{
	public int Id { get; set; }
	public string Name { set; set; }
	public virtual ICollection<Class> Students { get; set; }
}
```

And we have two DTOs: *StudentModel* and *ClassModel*.

```
public class StudentModel
{
	public int Id { get; set; }
	public string Name { get; set; }
}

public class ClassModel
{
	public int Id { get; set; }
	public string Name { get; set; }
	public List<StudentModel> Students { get; set; }
}
```

There are already two obvious errors you can make here: having code duplication generate the *StudentModel* depending on the entry point of the code, or having to pull all the properties out of the database (in this case not a big deal) in order to model your DTOs in memory.

```
/* Utilizing LINQ to Entities you can see 
*  how we have to specify StudentModels mappings 
*  in the class models select statement
*/ 
return database.Classes
	.Select(cl => new ClassModel
	{
		Id = cl.Id
		Students = cl.Students.Select(st =>	new StudentModel
		{
			Id = st.Id
		}
	})
	.ToList()
	
// Or we could pull all the data in order to get a cleaner modeling
return database.Classes
	.ToList()
	.Select(cl => new ClassModel(cl))
	.ToList()
```

Alternatively, you can generate expression trees to handle the modeling, which allows you to focus on specifying the binding between the data and the property. This is exactly how the first *Select* above operates, the difference is we can build the trees in order to reuse pieces throughout the system.

So let's take that same *ClassModel* and generate its expression tree like so:

```
public class ClassModel
{
	public int Id { get; set; }
	public string Name { get; set; }
	public List<StudentModel> Students { get; set; }

	/* 
     * Here we define how a the source of 
     * the object is bound to this object
     * The passed in expression is the parameter of 
     * select statement, in this case will be the type of Class
     */
	public static MemberAssignment[] Assignments(Expression param)
	{
		return new MemberAssignment[]
		{
		   /* 
            * Here we are saying we want to bind the property Id on 
            * ClassModel to the property Id 
            * on the passed param (Class)
            */
			Expression
                .Bind(
                    typeof(ClassModel).GetProperty("Id"), 
                    Expression.Property(param, "Id")
                ),
		        // Same for name as above			 
                        Expression
                .Bind(
                    typeof(ClassModel).GetProperty("Name"), 
                    Expression.Property(param, "Name")
                ),
			/* 
            * Here it gets a bit tricker, we are still binding 
            * students to students but we need to get a list 
            * (See StudentModel)
            */
			Expression
                 .Bind(
                    typeof(ClassModel).GetProperty("Students"), 
                    StudenModel.InitList(
                        Expression.Property(param, "Students")
                    )
                 )
		};
	}

    /*
     * Here is how the ClassModel is "newed" it creates 
     * a new ClassModel with a parameterless constructor
     * and binds all the known bindings to body, generating
     * new ClassModel { // Insert Bindings }
     */
	public static Expression<Func<Class, ClassModel>> Init()
	{
	    // Define the soruce of the expressions type
        // in this case class dbo
	    ParameterExpression param = 
                  Expression.Parameter(typeof(Class), "source");
		// New the class model using the above bindings
        MemberInitExpression init = 
             Expression.MemberInit(
                Expression.New(typeof(ClassModel)), 
                Assignments(param)
             );
		// Return the lambda which is 
        // source => new ClassModel { Id = source.Id, Name = source.Name }
	    return Expression.Lambda<Func<Class, ClassModel>>(init, param);
	}

}
```

In the above code, we can create reusable expression trees to model *ClassModel* no matter the data source, by specifying how a *Class* object binds to a *ClassModel* object - as long as we have a mapping. This becomes pretty apparent when we look at the *StudentModel*.

```
public class StudentModel
{
	public int Id { get; set; }
	public string Name { get; set; }

	// Same as the class model define the assignments
	public static MemberAssignment[] Assignments(Expression param)
	{
		return new MemberAssignment[]
		{
            /*
			* Here we are saying we want to bind the property Id 
            * on StudentModel to the property Id 
            * on the passed param (Student)
            */
			Expression.Bind(
                     typeof(StudentModel).GetProperty("Id"), 
                     Expression.Property(param, "Id")
            ),
			// Same for name as above
			Expression.Bind(
                     typeof(StudentModel).GetProperty("Name"), 
                     Expression.Property(param, "Name")
            ),
		};
	}
	
   // Just init a single StudentModel
   public static Expression<Func<Student, StudentModel>> Init()
   {
     ParameterExpression param = 
           Expression.Parameter(typeof(Student), "source");
     MemberInitExpression init = 
           Expression.MemberInit(
               Expression.New(typeof(StudentModel)), 
               Assignments(param)
           );
     return Expression.Lambda<Func<Student, StudentModel>>(init, param);
   }

    /*
     * This is a bit out of the scope of the article, 
     * but I wanted to include it to show the capablities  
     * Basically here we are generating the sub tree 
     * that will select and to list all the students in the class
     */
	public static MethodCallExpression InitList(Expression param)
	{
        /*
        * Just like the member init we have to bind 
        * the source of the select statement 
        * to the type of Student
        */
		ParameterExpression studentParam = 
           Expression.Parameter(typeof(Student), "source");

        /*
		* Next we new a student model with the assigments from above
	    * So now we have 
        * new StudentModel{ Id = source.Id, Name = soruce.Name }
        */
		MemberInitExpression studentModelInit = 
           Expression.MemberInit(
               Expression.New(typeof(StudentModel)), 
               Assignments(studentParam)
           );

        /*
		* And finally smack these two together in a 
        * lambda statement to generate
		* source => 
        * new StudentModel{ Id = source.Id, Name = soruce.Name }
        */
		LambdaExpression lambda = 
           Expression.Lambda<Func<Student, StudentModel>> 
                (studentModelInit, studentParam);

        /*
		* Now we have to specify we are making a call 
        * to the select statement and specify the types
        */
		MethodCallExpression studentSelect = Expression.Call(
			null,
			GetSelect().MakeGenericMethod(new Type[] { 
				// Our select "in"
				typeof(Student), 
				// Our select "out"
				typeof(StudentModel) 
				}),
			new Expression[] { 
				// The binding param (a list of students)
				param, 
				// The lamda body from above
				lambda 
				});

		// And finally to list it to get all of them
		return Expression.Call(
			typeof(Enumerable),
			"ToList",
			// Same here define the type of the out
			new Type[] { typeof(StudentModel) },
			// The "body" of the to list
			studentSelect
			);
	}
}
```

Now that we have all the bindings defined, we can make queries as so:

```
return database.Classes
	.Select(ClassModel.Init())
	.ToList();
	
return database.Students
	.Select(StudentModel.Init())
	.ToList()
```

---

You might be thinking, "Wow, that is a lot of extra code and overhead in order to generate models" and I thought the same thing, which is why I created [modelLINQ](https://github.com/GrizzlyEnglish/modelLINQ) (there are alternatives like [AutoMapper](https://automapper.org/)).
Using modelLINQ you can write smaller easier-to-digest modeling systems in order to create maintainable code. For example, I swapped the above code using modelLINQ.

```
public class StudentModel
{
	public int Id { get; set; }
	public string Name { get; set; }

   /*
	* All we need to do is setup the init function
	* I like to use From<Source> syntax
    */
	public static Func<Expression, MemberAssignments[]> FromStudent() 
     param =>
		new MemberAssigment[] {
			// Directly bind from source to result on both properties
			param.DirectBind<StudentModel>("Id"),
			param.DirectBind<StudentModel>("Name")
		};
}

public class ClassModel
{
	public int Id { get; set; }
	public string Name { get; set; }
	public List<StudentModel> Students { get; set; }
  
	public static Func<Expression, MemberAssignments[]> FromClass() 
        param =>
		new MemberAssigment[] {
			// Directly bind from source to result on both properties
			param.DirectBind<StudentModel>("Id"),
			param.DirectBind<StudentModel>("Name"),

            /*
			* Creates a list of StudentModels
			* We have to specify the binding model, the source, 
            * and the result in order to generate the list mapping
            */
			param.BindSelectedList<ClassModel, Class, StudentModel>
			    ("Students", "Students", StudentModel.FromStudent)
		};
}

// How to query
database.Class
  .Select(ClassModel.FromClass.Model<Class, ClassModel>())
  .ToList()
```

Thanks for reading; I hope someone finds it useful! If you have any thoughts, or believe I am wrong, please comment I'd love to hear your feedback!