Интерпретировать лямбда-выражение для пользовательского выбора

Прежде чем идти дальше, мне нужно немного рассказать об истории вопроса: у меня есть проект, который использует 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);
}
Другие вопросы по тегам