Expression.Lambda <Func <T, bool >> говорит, что 'T' не может быть найден - PullRequest
0 голосов
/ 10 января 2012

Я пытаюсь создать универсальный класс команд Exists (), который может создать предложение where для ЛЮБОЙ комбинации открытых свойств (полей) ЛЮБОГО класса LinqToSQL (таблиц) в моем тексте данных (базе данных).

Если вы написали следующее на основе фрагментов из MSDN и обнаружили, что я не могу успешно выполнить код, если я не объявил явно тип сущности (в данном случае MyApp.Data.Organization) Expression.Lambdaпараметр выражения Expression.Call.

Другими словами,

Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe }));

не работает, но

Expression.Lambda<Func<MyApp.Data.Organization,bool>>(predBody, new ParameterExpression[] { pe }));

работает.Я хочу что-то похожее на первое, поэтому метод остается общим для всех классов LinqToSQL в моем контексте данных.

Я проверил, что весь код до оператора Expression.Call работает нормально и генерирует правильный предикат.Что мне нужно изменить, чтобы сохранить параметр Type универсальным, чтобы тот же код работал для любого класса LinqToSQL в контексте данных?

Revision for Clarity Кажется, я используюT как переменная сбивала с толку.Вот пересмотренный код:

OrganizationCriteria criteria = new OrganizationCriteria { OrganizationId = 2 };
 using (var ctx = ContextManager<MyApp.Data.MyAppDataContext>.GetManager(myConnectionString, false)) {
                IQueryable tbl = ctx.DataContext.GetTable(criteria.EntityType).AsQueryable();
                Type tblType = tbl.ElementType;

                ParameterExpression pe = Expression.Parameter( tblType, "Item" );

                Expression left;
                Expression right;
                Expression prev = null;
                Expression curr = null;
                Expression predBody = null;
                foreach ( KeyValuePair<string, object> kvp in criteria.StateBag ) {
                    prev = curr;
                    object val = kvp.Value;
                    if (val is System.String ) {
                        left = Expression.Call( pe, typeof( string ).GetMethod( "ToLower", System.Type.EmptyTypes ) );
                        right = Expression.Constant( kvp.Value.ToString() );
                    }
                    else {
                        left = Expression.Property( pe, tblType.GetProperty( kvp.Key ) );
                        right = Expression.Constant( kvp.Value, tblType.GetProperty( kvp.Key ).PropertyType );
                    }
                    curr = Expression.Equal( left, right );

                    if ( prev != null ) {
                        predBody = Expression.AndAlso( prev, curr );
                    }
                    else {
                        predBody = curr;
                    }
                }

                MethodCallExpression whereCall = Expression.Call(
                    typeof( Queryable ),
                    "Where",
                    new Type[] { tblType },
                    tbl.Expression,
                    Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe }));

                var results = tbl.Provider.CreateQuery( whereCall );
}

Ответы [ 5 ]

1 голос
/ 10 января 2012

В вашем коде T - это переменная, а не тип; Вы не можете использовать переменную в качестве параметра универсального типа (даже если это переменная типа Type)

0 голосов
/ 11 января 2012

Ответ заключается в использовании неуниверсальной перегрузки Expression.Lambda ().Таким образом, замена:

Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe })

на

Expression.Lambda(predBody, new ParameterExpression[] { pe })

сделала свое дело, и теперь у меня есть полностью универсальный метод для запроса любой таблицы в любых полях.

0 голосов
/ 11 января 2012

Поскольку tblType известен только во время выполнения, вы можете вызвать Expression.Lambda<Func<T, bool>>() только с помощью отражения. Предполагая, что вы знаете, как получить правильный MethodInfo для требуемой перегрузки статического метода Lambda:

var funcType = typeof(Func<,>).MakeGenericType(tblType, typeof(bool));
var genericMethodInfo = typeof(Expression).GetMethod("Lambda", ...
var methodInfo = genericMethodInfo.MakeGenericMethod(funcType);
var expression = (Expression)methodInfo.Invoke(...

но получение правильного MethodInfo далеко не тривиально со всеми этими перегрузками.

0 голосов
/ 11 января 2012

Вам нужно будет передать параметр типа в метод, содержащий ваш код (или как параметр типа для вашего класса):

void SomeMethod<T>() {
   ...       
    Expression.Lambda<Func<T,bool>> ...
   ...
}
0 голосов
/ 10 января 2012

Похоже, вы пытаетесь получить доступ к T как типу - помните, что это на самом деле параметр общего типа.Если вы хотите использовать фактический тип, используйте typeof(T).Например, эта строка:

ParameterExpression pe = Expression.Parameter(T, "Item");

должна выглядеть следующим образом:

ParameterExpression pe = Expression.Parameter(typeof(T), "Item");
...