Создать динамическое LINQ BinaryExpression, используя данные в связанных таблицах - PullRequest
0 голосов
/ 27 октября 2010

У меня есть приложение, в котором все запросы создаются динамически на основе простого сообщения данных, полученного службой WCF.Сообщение данных, проще говоря, представляет собой набор пар имя столбца / значение столбца с добавлением оператора, например, Equals, Less Than и т. Д.

Простое сообщение данных для ColumnName-Value-Operator

Name, Joe, Equals
Age, 35, Less Than
Occupation, Geek, Equals
Rate, 1000, Greater Than

и т.д ...

Я несколько успешно использовал динамические двоичные выражения, основанные на содержимом сообщения данных.

BinaryExpression expression = null;
ParameterExpression parameter = Expression.Parameter(typeof(MessageType), "p");

foreach (row in DataMessage)
{
    BinaryExpression exp = DataLib.MakeQueryFilter(typeof(MessageType),
    row.ColumnName,row.ColumnValue,column.DataOperator.ToString(), parameter);

    expression = expression == null ? exp : Expression.AndAlso(expression, exp);

    results = DataContext.MessageType.Where(Expression.Lambda<Func<Media, bool>>(expression, parameter));

}



public static BinaryExpression MakeQueryFilter(Type type, string propertyName, object value, string dataoperator, ParameterExpression parameter)
    {

    //var type = oType.GetType();
    object queryvalue = null;

    var property = type.GetProperty(propertyName);

    Type propertyType = property.PropertyType;
    if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
        propertyType = propertyType.GetGenericArguments()[0];


    // convert the value appropriately 
    if (propertyType == typeof(System.Int32))
        queryvalue = Convert.ToInt32(value);
    if (property.PropertyType == typeof(DateTime))
        queryvalue = Convert.ToDateTime(value);
    if (property.PropertyType == typeof(Double))
        queryvalue = Convert.ToDouble(value);
    if (property.PropertyType == typeof(String))
        queryvalue = Convert.ToString(value);
    if (property.PropertyType == typeof(Guid))
        queryvalue = new Guid(value.ToString());

    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var constantValue = Expression.Constant(queryvalue);

    Type[] types = new Type[2];
    types.SetValue(typeof(Expression), 0);
    types.SetValue(typeof(Expression), 1);

    var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
    var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });

    return equality2;

}

Проблема, с которой я сталкиваюсь, - это когда яхотите запросить отношение к значению в другой таблице и даже углубиться в две> n-ые связи.Примерно так:

Name, Joe, Equals
Age, 35, Less Than
Jobs.Occupation, Geek, Equals
Jobs.Occupation.Salary.Rate, 1000, Greater Than

У меня нет проблем с написанием запроса LINQ вручную:

var results = from m in DataContext.MessageType
              where m.Name == "Joe"
              & m.Age == 35
              & m.Jobs.Occupation == "Geek"
              & m.Jobs.Occupation.Salaray.Rate >= 1000
              select m;

Есть какие-нибудь указатели, как я могу динамически создать этот запрос?Любая помощь очень ценится.Спасибо.

Эрик С.

Ответы [ 2 ]

0 голосов
/ 19 ноября 2012

У меня была такая же проблема. Спасибо за ответ! Очень полезно!

Вы написали это:

caller
{
  BinaryExpression expression = null;
  ParameterExpression parameter = Expression.Parameter(typeof(DataContext.Table), "p");

  string columns = "Relation1.Relation2.Column1";
  string value = "ABC123";

  BinaryExpression exp 
    = MakeQueryFilter(typeof(DataContext.Table),  
      columns,value,'Equal', parameter);

  expression = expression == null ? exp : Expression.AndAlso(expression, exp);

  results = DataContext.Table.Where(
    Expression.Lambda<Func<Table, bool>>(expression, parameter));
}

public static BinaryExpression MakeQueryFilter(Type type, string propertyNames, object value, string dataoperator, ParameterExpression parameter)
{

  string[] acolumns = columns.Split('.');

  var property = typeof (MediaEvent).GetProperty(acolumns[0]);
  MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);

  Type propertyType = property.PropertyType;
  if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
   propertyType = propertyType.GetGenericArguments()[0];

  if (acolumns.Length > 1)
  {
    for (int i = 1; i < acolumns.Length; i++)
    {
       propertyAccess = Expression.MakeMemberAccess(propertyAccess, propertyAccess.Type.GetProperty(acolumns[i]));
     }
  }

  object queryvalue = null;
  // convert the value appropriately 
  if (propertyType == typeof(System.Int32))
   queryvalue = Convert.ToInt32(value);
  if (property.PropertyType == typeof(DateTime))
   queryvalue = Convert.ToDateTime(value);
  if (property.PropertyType == typeof(Double))
   queryvalue = Convert.ToDouble(value);
  if (property.PropertyType == typeof(String))
   queryvalue = Convert.ToString(value);
  if (property.PropertyType == typeof(Guid))
   queryvalue = new Guid(value.ToString());

  var constantValue = Expression.Constant(queryvalue);

  Type[] types = new Type[2];
  types.SetValue(typeof(Expression), 0);
  types.SetValue(typeof(Expression), 1);

  var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
  var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });

  return equality2;

 }

Может быть, лучше, если вы напишите это:

public static BinaryExpression MakeQueryFilter<T>(string propertyNames, object value, string dataoperator, ParameterExpression parameter) where T : class
    {

        string[] props = propertyNames.Split('.');

        var property = typeof(T).GetProperty(props[0]);
        MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);

        if (props.Length > 1)
        {
            for (int i = 1; i < props.Length; i++)
            {
                property = propertyAccess.Type.GetProperty(props[i]);
                propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
            }
        }

        Type propertyType = property.PropertyType;
        if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
            propertyType = propertyType.GetGenericArguments()[0];

        object queryvalue = null;
        // convert the value appropriately 
        if (propertyType == typeof(System.Int32))
            queryvalue = Convert.ToInt32(value);
        if (property.PropertyType == typeof(DateTime))
            queryvalue = Convert.ToDateTime(value);
        if (property.PropertyType == typeof(Double))
            queryvalue = Convert.ToDouble(value);
        if (property.PropertyType == typeof(String))
            queryvalue = Convert.ToString(value);
        if (property.PropertyType == typeof(Guid))
            queryvalue = new Guid(value.ToString());

        var constantValue = Expression.Constant(queryvalue);

        Type[] types = new Type[2];
        types.SetValue(typeof(Expression), 0);
        types.SetValue(typeof(Expression), 1);

        var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
        var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });

        return equality2;

    }
0 голосов
/ 29 октября 2010

Обратите внимание на использование расширяющегося PropertyAccess.

{
  BinaryExpression expression = null;
  ParameterExpression parameter = Expression.Parameter(typeof(DataContext.Table), "p");

  string columns = "Relation1.Relation2.Column1";
  string value = "ABC123";

  BinaryExpression exp = MakeQueryFilter(typeof(DataContext.Table),  columns,value,'Equal', parameter);

  expression = expression == null ? exp : Expression.AndAlso(expression, exp);

  results = DataContext.Table.Where(Expression.Lambda<Func<Table, bool>>(expression, parameter));
}

public static BinaryExpression MakeQueryFilter(Type type, string propertyNames, object value, string dataoperator, ParameterExpression parameter)
{

  string[] acolumns = columns.Split('.');

  var property = typeof (MediaEvent).GetProperty(acolumns[0]);
  MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);

  Type propertyType = property.PropertyType;
  if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
   propertyType = propertyType.GetGenericArguments()[0];

  if (acolumns.Length > 1)
  {
    for (int i = 1; i < acolumns.Length; i++)
    {
       propertyAccess = Expression.MakeMemberAccess(propertyAccess, propertyAccess.Type.GetProperty(acolumns[i]));
     }
  }

  object queryvalue = null;
  // convert the value appropriately 
  if (propertyType == typeof(System.Int32))
   queryvalue = Convert.ToInt32(value);
  if (property.PropertyType == typeof(DateTime))
   queryvalue = Convert.ToDateTime(value);
  if (property.PropertyType == typeof(Double))
   queryvalue = Convert.ToDouble(value);
  if (property.PropertyType == typeof(String))
   queryvalue = Convert.ToString(value);
  if (property.PropertyType == typeof(Guid))
   queryvalue = new Guid(value.ToString());

  var constantValue = Expression.Constant(queryvalue);

  Type[] types = new Type[2];
  types.SetValue(typeof(Expression), 0);
  types.SetValue(typeof(Expression), 1);

  var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
  var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });

  return equality2;

 }
...