c# - Rewriting Linq Select to a new subpath -


i trying dynamically build linq queries. want able reuse linq expressions in other linq expressions. example:

    public class dummy     {          public string test { get; set; }         public sub sub { get; set; }     }       public class sub     {         public static expression<func<sub, string>> converter         {                         {                 return x => x.id + ": " + x.text;             }         }          public int id { get; set; }         public string text { get; set; }     } 

when writing converter dummy class, converter should able reuse sub.converter. thi spurpose have written dynamicselect<> extension method:

var result = query                 .where(x=>x.sub != null)                 .dynamicselect(x => new result())                     .select(x => x.subtext, x => x.sub,sub.converter)                 .apply()             .tolist(); 

dynamicselect creates new selectionbuilder, select part takes first input targetproperty (result.subtext), second property input property want convert , third input converter sub property. .apply call returns builtup expression tree.

i managed working simpler usecase (without subpath):

var result = query.dynamicselect(x => new result())                 .select(x => x.resolvedtest, x => x.inner == null ? x.test : x.inner.test)                 .select(x => x.subtext, x => x.sub == null ? null : x.sub.text)                 .apply()                 .tolist(); 

but how rebase expression subpath?

code far:

public static class selectbuilder {     public static linqdynamicconverter<tinput,toutput> dynamicselect<tinput,toutput>(         iqueryable<tinput> data,         expression<func<tinput, toutput>> initialconverter) toutput : new()     {         return new linqdynamicconverter<tinput,toutput>(data, initialconverter);     } }  public class linqdynamicconverter<tinput,toutput> toutput: new() {     #region inner classes      private class memberassignmentvisitor : expressionvisitor     {         private idictionary<memberinfo, memberassignment> singlepropertytobinding { get; set; }          public memberassignmentvisitor(idictionary<memberinfo, memberassignment> singlepropertytobinding)         {             singlepropertytobinding = singlepropertytobinding;         }          protected override memberassignment visitmemberassignment(memberassignment node)         {             singlepropertytobinding[node.member] = node;             return base.visitmemberassignment(node);         }     }      private class memberinfovisitor : expressionvisitor     {         internal memberinfo singleproperty { get; private set; }          public memberinfovisitor()         {         }          protected override expression visitmember(memberexpression node)         {             singleproperty = node.member;              return base.visitmember(node);         }     }      #endregion     #region properties      private iqueryable<tinput> data { get;set; }     private expression<func<tinput, toutput>> initialconverter { get;set;}     private idictionary<memberinfo, memberassignment> singlepropertytobinding { get; set; }      #endregion      #region constructor      internal linqdynamicconverter(iqueryable<tinput> data,         expression<func<tinput, toutput>> initialconverter)     {         data = data;          initialconverter = x => new toutput(); // start clean plate         var replace = initialconverter.replace(initialconverter.parameters[0], initialconverter.parameters[0]);         singlepropertytobinding = new dictionary<memberinfo, memberassignment>();         memberassignmentvisitor v = new memberassignmentvisitor(singlepropertytobinding);         v.visit(initialconverter);      }      #endregion      public linqdynamicconverter<tinput,toutput> select<tproperty,tconverted>(         expression<func<toutput, tconverted>> initializedoutputproperty,         expression<func<tinput, tproperty>> subpath,         expression<func<tproperty, tconverted>> subselect)     {         //????          return this;     }      // 1 works     public linqdynamicconverter<tinput,toutput> select<tproperty>(         expression<func<toutput, tproperty>> initializedoutputproperty,         expression<func<tinput, tproperty>> subselect)     {         var miv = new memberinfovisitor();         miv.visit(initializedoutputproperty);         var mi = miv.singleproperty;          var param = initialconverter.parameters[0];         expression<func<tinput, tproperty>> replace = (expression<func<tinput, tproperty>>)subselect.replace(subselect.parameters[0], param);         var bind = expression.bind(mi, replace.body);         singlepropertytobinding[mi] = bind;          return this;     }      public iqueryable<toutput> apply()     {         var converter = expression.lambda<func<tinput, toutput>>(             expression.memberinit((newexpression)initialconverter.body, singlepropertytobinding.values), initialconverter.parameters[0]);         return data.select(converter);     } } 

found solution. needed write new expression visitor added member acces(es):

    /// <summary>     /// rebinds full expression tree new single property     /// example: x => x.sub subpath. visitor starts visiting new     /// expression x => x.text. result should x => x.sub.text.     /// traversing member accesses starts rightmost side working toward parameterexpression.     /// when reach parameterexpression have inject whole subpath including parameter of subpath     /// </summary>     /// <typeparam name="tconverted"></typeparam>     /// <typeparam name="tproperty"></typeparam>     private class linqrebindvisitor<tconverted, tproperty> : expressionvisitor     {         public expression<func<tinput, tconverted>> resultexpression { get; private set; }         private parameterexpression subpathparameter { get; set; }         private expression subpathbody { get; set; }         private bool initialmode { get; set; }          public linqrebindvisitor(expression<func<tinput, tproperty>> subpath)         {             subpathparameter = subpath.parameters[0];             subpathbody = subpath.body;             initialmode = true;         }          protected override expression visitmember(memberexpression node)         {             // note cannot overwrite visitparameter because method must return parameterexpression             // whenever detect our next expression parameterexpression, inject subtree             if (node.expression parameterexpression && node.expression != subpathparameter)             {                 var expr = visit(subpathbody);                 return expression.makememberaccess(expr, node.member);             }             return base.visitmember(node);         }          protected override expression visitlambda<t>(expression<t> node)         {             bool initialmode = initialmode;             initialmode = false;             expression<t> expr = (expression<t>)base.visitlambda<t>(node);             if (initialmode)             {                 resultexpression = expression.lambda<func<tinput, tconverted>>(expr.body,subpathparameter);             }             return expr;         }     } 

the single property select method rather trivial:

    public linqdynamicconverter<tinput,toutput> select<tproperty,tconverted>(         expression<func<toutput, tconverted>> initializedoutputproperty,         expression<func<tinput, tproperty>> subpath,         expression<func<tproperty, tconverted>> subselect)     {         linqrebindvisitor<tconverted, tproperty> rebindvisitor = new linqrebindvisitor<tconverted, tproperty>(subpath);         rebindvisitor.visit(subselect);         var result = rebindvisitor.resultexpression;         return property<tconverted>(initializedoutputproperty, result);     } 

Comments

Popular posts from this blog

How has firefox/gecko HTML+CSS rendering changed in version 38? -

android - CollapsingToolbarLayout: position the ExpandedText programmatically -

Listeners to visualise results of load test in JMeter -