Как сделать запрос, не имея универсального типа? - PullRequest
1 голос
/ 26 октября 2011

Я начну с кода:

var objectType = typeof(Department); // Department is entity class from linqdatacontext

using (var dataContext = new DataModel.ModelDataContext())
{
    var entity = Expression.Parameter(objectType, "model");
    var keyValue = Expression.Property(entity, "Id");
    var pkValue = Expression.Constant(reader.Value);
    var cond = Expression.Equal(keyValue, pkValue);
    var table = dataContext.GetTable(objectType);
    ... // and here i don't how to proceed
}

Я даже не уверен, правильно ли я строю это выражение.Однако, проще говоря, мне нужно динамически вызывать SingleOrDefault() для этой таблицы, чтобы найти сущность по первичному ключу.Каждый найденный мной пример использует общий вариант GetTable<>(), но я не могу использовать это, очевидно.Я, наверное, что-то упускаю из виду ...

Ответы [ 4 ]

1 голос
/ 26 октября 2011

Всякий раз, когда я строю деревья выражений, мне нравится начинать с примера того, что я строю:

() => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == 1);

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

using(var dataContext = new DataModel.ModelDataContext())
{
    var getTableCall = Expression.Call(
        Expression.Constant(dataContext),
        "GetTable",
        new[] { entityType });

    var entity = Expression.Parameter(entityType, "entity");

    var idCheck = Expression.Equal(
        Expression.Property(entity, "Id"),
        Expression.Constant(reader.Value));

    var idCheckLambda = Expression.Lambda(idCheck, entity);

    var singleOrDefaultCall = Expression.Call(
        typeof(Queryable),
        "SingleOrDefault",
        new[] { entityType },
        getTableCall,
        Expression.Quote(idCheckLambda));

    var singleOrDefaultLambda = Expression.Lambda<Func<object>>(
        Expression.Convert(singleOrDefaultCall, typeof(object)));

    var singleOrDefaultFunction = singleOrDefaultLambda.Compile();

    return singleOrDefaultFunction();    
}

Мы должны преобразовать вызов SingleOrDefault, чтобы иметьвозвращаемый тип объекта, поэтому он может служить телом функции Func<object>.

(не проверено)

Редактировать: параметризация контекста и значения данных

Теперь мы создаем эту функцию:

(dataContext, value) => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == value);

Вы бы изменили константы на параметры и добавили эти параметры к функции, которую вы компилируете:

var dataContextParameter = Expression.Parameter(typeof(ModelDataContext), "dataContext");

var valueParameter = Expression.Parameter(typeof(object), "value");

var getTableCall = Expression.Call(
    dataContextParameter,
    "GetTable",
    new[] { entityType });

var entity = Expression.Parameter(entityType, "entity");

var idCheck = Expression.Equal(
    Expression.Property(entity, "Id"),
    valueParameter);

var idCheckLambda = Expression.Lambda(idCheck, entity);

var singleOrDefaultCall = Expression.Call(
    typeof(Queryable),
    "SingleOrDefault",
    new[] { entityType },
    getTableCall,
    Expression.Quote(idCheckLambda));

var singleOrDefaultLambda =
    Expression.Lambda<Func<ModelDataContext, object, object>>(
        Expression.Convert(singleOrDefaultCall, typeof(object)),
        dataContextParameter,
        valueParameter);

var singleOrDefaultFunction = singleOrDefaultLambda.Compile();

// Usage

using(var dataContext = new DataModel.ModelDataContext())
{
    return singleOrDefaultFunction(dataContext, reader.Value);    
}
1 голос
/ 26 октября 2011

Если вы используете .NET 4, вы можете попробовать преобразовать ваши возвращенные объекты в dynamic, чтобы вы могли запросить их следующим образом.

using (var dataContext = new DataModel.ModelDataContext())
{
   var entity = Expression.Parameter(objectType, "model");
   var keyValue = Expression.Property(entity, "Id");
   var pkValue = Expression.Constant(reader.Value);
   var cond = Expression.Equal(keyValue, pkValue);
   var table = dataContext.GetTable(objectType);

   var result = table.Where(ent => ((dynamic)ent).SomeField == "SomeValue");
}
0 голосов
/ 26 октября 2011

Я бы сделал это, используя Reflection и динамическую библиотеку запросов LINQ , лично.

Вы можете получить список всех ключей для таблицы с помощью dataContext.Mapping.GetMetaType(objectType).IdentityMembers, а затем получить доступ к данным с чем-то вроде dataContext.GetTable(objectType).Where(key.Name + "==@0", id).

Очевидно, я пропустил несколько шагов - если у вас есть несколько ключей, вам нужно создать более полный предикат с циклом более .IdentityMembers, и если у вас всегда есть только один ключ, вы можете использовать .First () на нем. Я тоже не проверял, но это должно быть довольно близко. Вероятно, это будет всего 6-7 строк кода - я могу написать (и проверить), если вам это нужно.


Редактировать: библиотеку динамических запросов LINQ можно загрузить с веб-сайта Microsoft по адресу http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx - просто включите DynamicLINQ.cs в свой проект, и все хорошо.

0 голосов
/ 26 октября 2011

Я все еще не совсем уверен относительно всей вашей проблемы (и я подозреваю, что ответ на вопрос dynamic также решит часть того, что произойдет).Тем не менее, просто для ответа:

Каждый найденный мной пример использует универсальный вариант GetTable <> (), но я не могу использовать это, очевидно

Для любого T, Table<T> реализует (среди прочих интерфейсов) ITable<T> и ITable.Первый тип типизирован, второй - нет.

Форма GetTable<T>() возвращает такой Table<T>.Однако форма GetTable(Type t) возвращает ITable.Поскольку ITable наследуется от IQueryable, вы можете запросить его.Если вам нужно что-то сделать с этим запросом, который обычно требует знания типа (например, сравнение с данным свойством), тогда dynamic согласно предыдущему ответу Стива Даннера позволяет это сделать.

...