Интерпретировать лямбда-выражение для пользовательского выбора
Прежде чем идти дальше, мне нужно немного рассказать об истории вопроса: у меня есть проект, который использует nHibernate и некоторый универсальный репозиторий для пересылки предикатов и возврата сущностей следующим образом:
public abstract class GenericRepository< T >
{
...
public virtual T Single( Expression< Func< T, bool > > predicates )
{
// Get object from nHibernate session object
var retObj = Session
.Query< T >()
.Where( predicates ).SingleOrDefault();
return retObj;
}
...
}
Затем я могу, например, получить объект таким образом:
var entity = Context.MyGenericEntityRepository.Single( e => e.Id == id );
// or
var entity = Context.MyGenericEntityRepository.Single( e => e.Name == name );
Но у меня также есть некоторые сущности, которые из-за характера проекта сохраняются не в базу данных, а в файловую систему (набор файлов). Поэтому я использую производный репозиторий, который использует некоторый класс DataAccess для получения сущностей из файловой системы следующим образом:
public class NotGenericRepository
{
...
// for example
public IList<Entity> All()
{
return _entityDataAccess.All();
}
...
}
Как уже говорилось, сущность 2-го типа не хранится в базе данных, однако, чтобы облегчить мою поездку, я создал своего рода систему баз данных в памяти, используя DataSets и DataTables. Поэтому, когда решение запускается впервые, у меня есть одноэлементная база данных CustomDatabase, которая инициализируется, создает в памяти DataTables, добавляет отношения между DataTalbes и добавляет их в общий DataSet перед сканированием файловой системы для заполнения таблиц.
Используя это, я теперь могу каждый раз запрашивать мои таблицы данных вместо сканирования по дереву файловой системы. Затем я настроил некоторые события в моей базе данных CustomDatabase, чтобы при добавлении / удалении / обновлении строки изменения отражались в файловой системе.
Итак... Это для фона, извините за длину...
Мой вопрос теперь довольно прост, я ищу способ каким-то образом перевести лямбда-выражение, пересылаемое репозиторием, в мой класс DataAccess, чтобы я мог затем проанализировать его и выбрать из своих DataTables соответственно...
Например, вызов в хранилище, такой как:
var entity = Context.MyNotGenericEntityRepository.Single( e => e.Id == id );
// or
var entity = Context.MyNotGenericEntityRepository.Single( e => e.Name == name );
Должен быть переведен в DataAccess как:
DataRow entityRow = CustomDatabase.Tables[ "EntityName" ].AsEnumerable().Where( e => e.Field< Guid >( "Id" ) == id);
// or
DataRow entityRow = CustomDatabase.Tables[ "EntityName" ].AsEnumerable().Where( e => e.Field< string >( "Name" ) == name);
Или же:
var entity = from myRow in CustomDatabase.Tables[ "EntityName" ].AsEnumerable()
where myRow.Field<Guid>( "Id" ) == id
select myRow;
// or
var entity = from myRow in CustomDatabase.Tables[ "EntityName" ].AsEnumerable()
where myRow.Field<string>( "Name" ) == name
select myRow;
Я абсолютно не знаю, как это сделать, я искал всю сеть, но проблема в том, что я не знаю, как назвать эту проблему, поэтому я не нашел много до сих пор...:(
Любая помощь приветствуется, так как я ожидаю, что будет несколько способов решить эту проблему:)
Спасибо!!
2 ответа
Трудно сказать, что было пропущено в примере с производным классом репозитория, но это может быть так же просто, как переопределение (или перегрузка) Single для возврата All().Single(..).
Предполагая, что выражения вашего предиката имеют форму в вашем вопросе, вы можете преобразовать выражение в одно для DataRow
, а затем используйте это для фильтрации таблицы, соответствующей сущности:
public static DataRow GetSingleRow<T>(Expression<Func<T, bool>> expr)
{
var bodyExpr = ((LambdaExpression)expr).Body;
var binExpr = (BinaryExpression)bodyExpr;
var propExpr = (MemberExpression)binExpr.Left;
PropertyInfo targetProperty = (PropertyInfo)propExpr.Member;
Type targetType = targetProperty.DeclaringType;
var valueExpr = binExpr.Right;
string entityName = targetType.Name;
var fieldMethod = typeof(DataRowExtensions).GetMethod("Field", new[] { typeof(DataRow), typeof(string) });
var methodInfo = fieldMethod.MakeGenericMethod(targetProperty.PropertyType);
var propNameExpr = Expression.Constant(targetProperty.Name);
var rowParamExpr = Expression.Parameter(typeof(DataRow), "dr");
var fieldInvocationExpr = Expression.Call(methodInfo, rowParamExpr, propNameExpr);
var eqExpr = Expression.Equal(fieldInvocationExpr, valueExpr);
Func<DataRow, bool> predicate = Expression.Lambda<Func<DataRow, bool>>(eqExpr, rowParamExpr).Compile();
return CustomDatabase.Tables[entityName].AsEnumerable().Single(predicate);
}