title: Dynamic LINQ Query to Expression Tree
body: <p>I am trying to return an entity with updated properties based on a condition in a LINQ query. Below is the query and is working fine. </p>
  
  <pre><code>    public class SampleDataModel
          {
              public string Name { get; set; }
              public string Group { get; set; }
              public int? Hyper { get; set; }
              public string Sin { get; set; }
              public int? Auto { get; set; }
  
              public string Comment
              {
                  get { return (Auto.HasValue ? "Hyper &amp; Name should be masked" : ""); }
              }
  
              public override string ToString()
              {
                  return $"Group={Group};Name={Name};Hyper={Hyper};Sin={Sin};Auto={Auto};Comment={Comment}";
              }
          }
  
          public class SampleData
          {
              private static readonly IList&lt;SampleDataModel&gt; Data = new List&lt;SampleDataModel&gt;();
  
              static SampleData()
              {
                  Data.Add(new SampleDataModel()
                      {Name = "Name 1", Group = "Group 11", Hyper = 10, Sin = "S12345986554", Auto = 1});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 2", Group = "Group 12", Hyper = 101, Sin = "MS7123452331", Auto = 19});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 3", Group = "Group 13", Hyper = 103, Sin = "SJK1234Z5666", Auto = 18});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 4", Group = "Group 14", Hyper = 210, Sin = "DFS125349811", Auto = 41});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 5", Group = "Group 15", Hyper = 150, Sin = "YUTS12345000", Auto = null});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 6", Group = "Group 16", Hyper = 106, Sin = "KTRS12Y345211", Auto = null});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 7", Group = "Group 17", Hyper = 510, Sin = "CVS12X342895", Auto = 23});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 8", Group = "Group 18", Hyper = 170, Sin = "ZASS12356545", Auto = 76});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 9", Group = "Group 19", Hyper = 1210, Sin = "QWES18782345", Auto = 11});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 10", Group = "Group 21", Hyper = 1450, Sin = "RTYS989812345", Auto = 91});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 11", Group = "Group 31", Hyper = 1067, Sin = "SDF134562345", Auto = null});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 12", Group = "Group 41", Hyper = 1087, Sin = "SHGF12376745", Auto = null});
                  Data.Add(new SampleDataModel()
                      {Name = "Name 13", Group = "Group 51", Hyper = 10912, Sin = "SKJU00012345", Auto = null});
              }
  
              public static IEnumerable&lt;SampleDataModel&gt; Get =&gt; Data;
          }
      public static class MaskService
          {
              public static IEnumerable&lt;SampleDataModel&gt; Mask(IEnumerable&lt;SampleDataModel&gt; data)
                  {
                      List&lt;SampleDataModel&gt; result = new List&lt;SampleDataModel&gt;();
  
                      var result1 = (from d in data
                          select new SampleDataModel()
                          {
                              Auto = d.Auto, Group = d.Group,
                              Sin = d.Sin,
                              Hyper = (!d.Auto.HasValue) ? d.Hyper : null,
                              Name = (!d.Auto.HasValue) ? d.Name : "XXXXXX"
  
                          });
  
                      result = result1.ToList&lt;SampleDataModel&gt;();
  
                      return result;
                  }
          }
      class Program
          {
              static void Main(string[] args)
              {
                  var data = SampleData.Get;
  
  
                  var maskedData = MaskService.Mask(data);
                  foreach (var model in maskedData)
                  {
                      Console.WriteLine($"{model}");
                  }
               }
          }
  </code></pre>
  
  <p>Anytime there is a change in the columns that needs an override then there is a change to the code either for condition or property override. To overcome this and to reuse the code I came up with generic solution based on some suggestions from stackoverflow, posts from google search and an example from Microsoft. </p>
  
  <p><strong>Generic Approach:</strong></p>
  
  <pre><code>    public static class MaskService
      {
          public static bool Contains&lt;T&gt;(this IEnumerable&lt;T&gt; source, string property) where T:IMaskCriteria
          {
              if (source == null || string.IsNullOrWhiteSpace(property))
                  return false;
  
              foreach (var maskCriteria in source)
              {
                  return maskCriteria.Contains(property);
              }
  
              return false;
          }
          public static IEnumerable&lt;MaskCondition&gt; Get&lt;T&gt;(this IEnumerable&lt;T&gt; source, string property) where T : IMaskCriteria
          {
              IEnumerable&lt;MaskCondition&gt; result = null;
              if (source == null || string.IsNullOrWhiteSpace(property))
                  return result;
  
              foreach (var maskCriteria in source)
              {
                  if (maskCriteria.Contains(property))
                  {
                      result = maskCriteria.Conditions;
                      break;
                  }
              }
  
              return result;
          }
  
          public static PropertyInfo Get(this PropertyInfo[] source, string property)
          {
              if (source == null || string.IsNullOrWhiteSpace(property))
                  return null;
              property = property.ToLower();
              foreach (var prop in source)
              {
                  if (prop.Name.ToLower().Equals(property))
                  {
                      return prop;
                  }
              }
  
              return null;
          }
          public static IEnumerable&lt;T&gt; Mask&lt;T&gt;(this IEnumerable&lt;T&gt; source, IEnumerable&lt;IMaskCriteria&gt; maskProperties) where T : new()
          {
              var result = source.Select(MaskFun&lt;T&gt;(maskProperties));
              return result;
          }
  
          public static Func&lt;T, T&gt; MaskFun&lt;T&gt;(IEnumerable&lt;IMaskCriteria&gt; maskProperties) where T : new()
          {
              if (maskProperties == null)
                  return null;
  
              Type typeofT = typeof(T);
              var model = Expression.New(typeofT);
              var inputLinqVar = Expression.Parameter(typeofT, "d");//input variable in linq query
              object GetDefault(Type type) =&gt; Nullable.GetUnderlyingType(type) != null ? null : type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : "XXXXXX";
              var maskCriterias = maskProperties as List&lt;IMaskCriteria&gt; ?? maskProperties.ToList();
  
              PropertyInfo[] props = typeofT.GetProperties();
              List&lt;MemberAssignment&gt; memberAssignments = new List&lt;MemberAssignment&gt;();
              foreach (var propertyInfo in props)
              {
                  if (!propertyInfo.CanWrite)
                      continue;
  
                  bool hasToBeMasked = maskCriterias.Contains(propertyInfo.Name);
                  var valueFromLinqVariable = Expression.Property(inputLinqVar, propertyInfo); //d.{propertyName} eg. d.Id
                  MemberAssignment memberAssignment = Expression.Bind(propertyInfo, valueFromLinqVariable);//{propertyName} = d.{propertyName} eg. Id = d.Id
                  if (hasToBeMasked)
                  {
                      List&lt;MaskCondition&gt; maskConditions = maskCriterias.Get(propertyInfo.Name) as List&lt;MaskCondition&gt;;
                      List&lt;Expression&gt; binConditions = new List&lt;Expression&gt;();
                      if (maskConditions != null)
                          foreach (var maskCondition in maskConditions)
                          {
                              var condPropInfo = props.Get(maskCondition.PropertyName);
                              Expression left = Expression.Property(inputLinqVar, condPropInfo);
                              Expression right = Expression.Constant(maskCondition.PropertyValue);
                              Expression e1 = null;
                              switch (maskCondition.Comparer)
                              {
                                  case MaskConditionComparer.Equals:
                                  {
                                      e1 = Expression.Equal(left, right);
                                      break;
                                  }
  
                                  case MaskConditionComparer.NotEquals:
                                  {
                                      e1 = Expression.NotEqual(left, right);
                                      break;
                                  }
  
                                  case MaskConditionComparer.GreaterThan:
                                  {
                                      e1 = Expression.GreaterThan(left, right);
                                      break;
                                  }
  
                                  case MaskConditionComparer.GreaterThanOrEquals:
                                  {
                                      e1 = Expression.GreaterThanOrEqual(left, right);
                                      break;
                                  }
  
                                  case MaskConditionComparer.LessThan:
                                  {
                                      e1 = Expression.LessThan(left, right);
                                      break;
                                  }
  
                                  case MaskConditionComparer.LessThanOrEquals:
                                  {
                                      e1 = Expression.LessThanOrEqual(left, right);
                                      break;
                                  }
                              }
  
                              binConditions.Add(e1);
                          }
  
                      ConstantExpression maskedValue = Expression.Constant(GetDefault(propertyInfo.PropertyType),
                          propertyInfo.PropertyType);
                      ConditionalExpression assignMaskValueIfMaskConditionTrue = null;
                      if (binConditions.Count == 1)
                      {
                          assignMaskValueIfMaskConditionTrue =
                              Expression.Condition(binConditions[0], maskedValue, valueFromLinqVariable);
                      }
                      else if (binConditions.Count &gt; 1)
                      {
                          Expression andExpression = binConditions[0];
                          //Expression getExp(Expression e1, Expression e2) =&gt; Expression.AndAlso(e1, e2);
                          for (var i = 1; i &lt; binConditions.Count; i++)
                          {
                              andExpression = Expression.AndAlso(andExpression, binConditions[i]);
                          }
  
                          assignMaskValueIfMaskConditionTrue =
                              Expression.Condition(andExpression, maskedValue, valueFromLinqVariable);
                      }
                      assignMaskValueIfMaskConditionTrue =
                      (ConditionalExpression)new ParameterReplacer(inputLinqVar).Visit(
                          assignMaskValueIfMaskConditionTrue);
                      var condExp = Expression.Lambda&lt;Func&lt;T, Object&gt;&gt;(assignMaskValueIfMaskConditionTrue, inputLinqVar);
  
                      memberAssignment = Expression.Bind(propertyInfo, condExp);
                  }
  
                  memberAssignments.Add(memberAssignment);
                  var message = hasToBeMasked ? "will be masked" : "no masking";
                  Console.WriteLine($"{propertyInfo.Name}-&gt;{propertyInfo.PropertyType.Name}==&gt;{message};{Nullable.GetUnderlyingType(propertyInfo.PropertyType)?.FullName}");
              }
  
              var modelInit = Expression.MemberInit(model, memberAssignments);// new T() { propertyName = d.propertyName} eg. new Emp() { Name = d.Name }
              var lambda = Expression.Lambda&lt;Func&lt;T, T&gt;&gt;(modelInit, inputLinqVar);
  
              return lambda.Compile();
          }
      }
      public class ParameterReplacer : ExpressionVisitor
      {
          private readonly ParameterExpression _parameter;
  
          protected override Expression VisitParameter(ParameterExpression node)
          {
              return base.VisitParameter(_parameter);
          }
  
          public ParameterReplacer(ParameterExpression parameter)
          {
              _parameter = parameter;
          }
      }
  
      public class PropertyComparer : IEqualityComparer&lt;string&gt;
      {
          public bool Equals(string x, string y)
          {
              if (string.IsNullOrWhiteSpace(x) || string.IsNullOrWhiteSpace(y))
                  return false;
              return (x.ToLower() == (y.ToLower()));
          }
  
          public int GetHashCode(string obj)
          {
              return obj.GetHashCode();
          }
      }
  
      public interface IMaskCriteria
      {
          List&lt;string&gt; ToBeMasked { get; set; }
  
          List&lt;MaskCondition&gt; Conditions { get; set; }
  
          bool Contains(string property);
      }
      public class MaskCriteria : IMaskCriteria
      {
          private  static readonly PropertyComparer comparer = new PropertyComparer();
  
          public MaskCriteria()
          {
              ToBeMasked = new List&lt;string&gt;();
              Conditions = new List&lt;MaskCondition&gt;();
          }
          public List&lt;string&gt; ToBeMasked { get; set; }
  
          public List&lt;MaskCondition&gt; Conditions { get; set; }
  
          public bool Contains(string property)
          {
              if (ToBeMasked == null)
                  return false;
  
              if (string.IsNullOrWhiteSpace(property))
                  return false;
  
              return ToBeMasked.Contains(property, comparer);
          }
  
      }
  
      public class MaskCondition
      {
          public string PropertyName { get; set; }
          public object PropertyValue { get; set; }
          public MaskConditionComparer Comparer { get; set; }
      }
  
      public enum MaskConditionComparer
      {
          Equals,
          NotEquals,
          GreaterThan,
          GreaterThanOrEquals,
          LessThan,
          LessThanOrEquals
      }
      class Program
      {
          static void Main(string[] args)
          {
              var data = SampleData.Get;
  
              Console.WriteLine("******************Start Masking Using Expression Trees In LINQ***********************");
              var mc = new MaskCriteria();
              mc.ToBeMasked.AddRange(new List&lt;string&gt;() { "name", "hyper" });
              mc.Conditions.Add(new MaskCondition(){ PropertyValue = null,PropertyName = "Auto", Comparer = MaskConditionComparer.Equals});
  
              maskedData = data.Mask&lt;SampleDataModel&gt;(new List&lt;MaskCriteria&gt;() {mc});
              foreach (var model in maskedData)
              {
                  Console.WriteLine($"{model}");
              }
              Console.WriteLine("******************End Masking Using Expression Trees In LINQ***********************");
              Console.ReadLine();
          }
      }
  
  </code></pre>
  
  <p>When I run the code, I am getting the following exception at <code>memberAssignment = Expression.Bind(propertyInfo, condExp);</code></p>
  
  <p>System.ArgumentException: 'Argument types do not match'</p>
  
  <p>If I remove switch case, then the properties Name and Hyper are getting XXXXX or null respectively. </p>
  
  <p>I have tried several ways with no luck and different exceptions. I never used Expression Trees before. So can someone please suggest on how to deal with conditional assignment?</p>