How to write LINQ query with column name as parameter in still safe way

I am looking for help on how to achieve this with LINQ in a safe way.

I need to search the Performance table with many columns. Based on the criteria specified for the search, I need to select the columns and search on these columns with the given values.

private static IQueryable<Investment> PerformanceSearch(IQueryable<Investment> investments, **??? searchColumn**, double minValue, double maxValue)
{
  var entity = ExtendedEntities.Current;

  investments = from inv in entity.Investments 
                join performance in entity.Performances on inv.InvestmentID equals perfromance.InvestmentID
                where **performance.searchColumn** >= minValue && **performance.searchColumn** = maxValue
  return investments;
}

Now I am turning to you for help:

  • How to pass the "searchColumn" column to this method in a safe way? I was thinking of creating a dictionary object in order to somehow support the column names from the entity framework. But not sure how to achieve this.

  • How to execute a LINQ query using the passed columnName element and apply the where clause.

If Else Switch case, , ...

/*
 * Search Columns can be:
 *      "Return1Month", "Return2Months", "Return3Months", ... almost 10 more and
 *      "Risk1Month", "Risk2Months", "Risk3Months", ... almost 10 more and
 *      "TrackingError1Month", "TrackingError2Months", "TrackingError3Months", ... almost 10 more and
 *      2 more similar set of columns ... 
 */

Stackoverflow, Microsoft Dynamic LINQ, . , , .

.

EDIT -

, - "".

+5
4

, LINQ - LINQ . , LINQ! LINQ , , , .

, LINQ , . , , switch, . System.Linq.Expressions :

EDIT: . investments , . EF ( _performance _investments).

    public static IQueryable<Investment> PerformanceSearch(Expression<Func<Performance, double>> searchColumn, double minValue, double maxValue) {

        // LINQ Expression that represents the column passed in searchColumn
        // x.Return1Month
        MemberExpression columnExpression = searchColumn.Body as MemberExpression;

        // LINQ Expression to represent the parameter of the lambda you pass in
        // x
        ParameterExpression parameterExpression = (ParameterExpression)columnExpression.Expression;

        // Expressions to represent min and max values
        Expression minValueExpression = Expression.Constant(minValue);
        Expression maxValueExpression = Expression.Constant(maxValue);

        // Expressions to represent the boolean operators
        // x.Return1Month >= minValue
        Expression minComparisonExpression = Expression.GreaterThanOrEqual(columnExpression, minValueExpression);

        // x.Return1Month <= maxValue
        Expression maxComparisonExpression = Expression.LessThanOrEqual(columnExpression, maxValueExpression);

        // (x.Return1Month >= minValue) && (x.Return1Month <= maxValue)
        Expression filterExpression = Expression.AndAlso(minComparisonExpression, maxComparisonExpression);

        // x => (x.Return1Month >= minValue) && (x.Return1Month <= maxValue)
        Expression<Func<Performance, bool>> filterLambdaExpression = Expression.Lambda<Func<Performance, bool>>(filterExpression, parameterExpression);

        // use the completed expression to filter your collection
        // This requires that your collection is an IQueryable.
        // I believe that EF tables are already IQueryable, so you can probably
        // drop the .AsQueryable calls and it will still work fine.
        var query = (from i in _investments
                     join p in _performance.AsQueryable().Where(filterLambdaExpression)
                       on i.InvestmentId equals p.InvestmentId
                     select i);

        return query.AsQueryable();

    } 

PerformanceSearch , :

    private static IList<Investment> _investments;
    private static IList<Performance> _performance;

    static void Main(string[] args) {

        // Simulate your two Entity Framework tables
        BuildMockDataset();

        // Return1Month is on Performance, but I return IQueryable<Investment>;
        var results = PerformanceSearch(x => x.Return1Month, 300, 1000);

    }

, double Performance searchColumn, min max double.

+4

, , Func < TIn, TOUT > ( ). generic , . ...

private static IQueryable<Investment> PerformanceSearch<TMember>(
                              IQueryable<Investment> investments, 
                              Func<Performance,TMember> SearchColumn, 
                              TMember minValue, 
                              TMember maxValue)
{
    var entity = ExtendedEntities.Current;

    investments = from inv in entity.Investments 
        join perfromance in entity.Performances on inv.InvestmentID equals perfromance.InvestmentID
        where SearchColumn(perfromance) >= minValue && SearchColumn(perfromance) <= maxValue
    return investments;
}

:

var results = PerformanceSearch<double>(investments, p => p.Return1Month, 10.0, 20.0);
+2
private static IQueryable<Investment> PerformanceSearch(IQueryable<Investment> investments, string searchColumn, double minValue, double maxValue)
{
  var entity = ExtendedEntities.Current;

  investments = from inv in entity.Investments 
                join perfromance in entity.Performances on inv.InvestmentID equals perfromance.InvestmentID
                where
                (
                    (searchColumn = "Return1Month" && perfromance.Return1Month >= minValue && perfromance.Return1Month <= maxValue) ||
                    (searchColumn = "Return2Months" && perfromance.Return2Months >= minValue && perfromance.Return2Months <= maxValue) ||
                    (searchColumn = "Return3Months" && perfromance.Return3Months >= minValue && perfromance.Return3Months <= maxValue) ||
                    (searchColumn = "Risk1Month" && perfromance.Risk1Month >= minValue && perfromance.Risk1Month <= maxValue)
                    // continue like this for as many columns, unless you want to use reflection
                )
  return investments;
}

- , , :

http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx

+1

, , :

var wheres = new Dictionary<string, Expression<Func<Performance, bool>>>()
{
    { "Return1Month", p => p.Return1Month >= minValue && p.Return1Month <= minValue },
    { "Return2Months", p => p.Return2Months >= minValue && p.Return2Months <= minValue },
    { "Return3Months", p => p.Return3Months >= minValue && p.Return3Months <= minValue },
    { "Risk1Month", p => p.Risk1Month >= minValue && p.Risk1Month <= minValue },
    { "TrackingError1Month", p => p.TrackingError1Month >= minValue && p.TrackingError1Month <= minValue },
    /* etc */
};

:

private static IQueryable<Investment> PerformanceSearch(IQueryable<Investment> investments, string searchColumn, double minValue, double maxValue)
{
    var entity = ExtendedEntities.Current;

    var wheres = new Dictionary<string, Expression<Func<Performance, bool>>>()
    {
        { "Return1Month", p => p.Return1Month >= minValue && p.Return1Month <= minValue },
        { "Return2Months", p => p.Return2Months >= minValue && p.Return2Months <= minValue },
        { "Return3Months", p => p.Return3Months >= minValue && p.Return3Months <= minValue },
        { "Risk1Month", p => p.Risk1Month >= minValue && p.Risk1Month <= minValue },
        { "TrackingError1Month", p => p.TrackingError1Month >= minValue && p.TrackingError1Month <= minValue },
        /* etc */
    };

    var investments = (
        from inv in entity.Investments 
        join perfromance in entity.Performances.Where(wheres[searchColumn]) on inv.InvestmentID equals perfromance.InvestmentID
        select inv;

    return investments;
}

Building a dictionary for each call is incredibly fast compared to the actual database call, so don't worry too much about it. If you decide to worry, make the dictionary a static private field.

+1
source

All Articles