Как я могу использовать ОБЪЕДИНЕНИЕ запроса запроса на n-recordsets, когда обзор var необходим?

Я не совсем понимаю, чего вы пытаетесь достичь с помощью прямого поколения IL; OrderByDescending принимает параметр Func<TSource, TKey> с именем «keySelector». Таким образом, единственным IL, который вы можете сгенерировать, все еще используя этот метод, будет просто обычный вызов метода, который передает аргумент «keySelector» методу OrderByDescending, если только вы не собираетесь повторно реализовать OrderByDescending в IL.

Есть ли причина, по которой вам нужно полностью перейти к IL?

Если это для кода уровня пользователя, вы можете «скомпилировать» expression, который был бы передан этому методу и обычно звоните OrderByDescending(), например,

var expression = /* Select Field/Property Expression*/;
array.OrderByDescending(expression.Compile()).ToArray();

Если это код уровня инфраструктуры / утилиты, вы могли бы избежать использования «Деревьев выражений», не переходя полностью к ручному IL. например,

public static FilterDelegate<T> CreateDelegate<T>(Expression<Func<T, double>> expression)
{
    var parameter = Expression.Parameter(typeof(IEnumerable<T>), "source");

    // Your `GetMethod` for OrderByDescending did not work for me,
    // so I'll just hand wave about this.
    var orderByDescMethod = typeof(Enumerable)
        .GetMethods()
        .Single(m => m.Name == nameof(Enumerable.OrderByDescending) &&
                     m.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(T), typeof(double));

    var toArrayMethod = typeof(Enumerable)
        .GetMethod(nameof(Enumerable.ToArray))
        .MakeGenericMethod(typeof(T));

    var orderByExpression = Expression.Call(orderByDescMethod, parameter, expression);
    var lambdaBody = Expression.Call(toArrayMethod, orderByExpression);
    var lambdaExpression = Expression.Lambda<FilterDelegate<T>>(lambdaBody, parameter);

    return lambdaExpression.Compile();
}

Если, тем не менее, по какой-то причине вам все равно необходимо излучать это напрямую через IL, то может сработать что-то вроде следующего.

public static FilterDelegate<T> CreateDelegate<T>(Expression<Func<T, double>> expression)
{
    // Your `GetMethod` for OrderByDescending did not work for me,
    // so I'll just hand wave about this.
    var orderByDescMethod = typeof(Enumerable)
                            .GetMethods()
                            .Single(m => m.Name == nameof(Enumerable.OrderByDescending) &&
                                         m.GetParameters().Length == 2)
                            .MakeGenericMethod(typeof(T), typeof(double));

    var toArrayMethod = typeof(Enumerable)
                        .GetMethod(nameof(Enumerable.ToArray))
                        .MakeGenericMethod(typeof(T));

    // TODO: if you don't already have one of these
    //       you'll probably want to pull this out and re-use it
    //       rather than making a new one for every delegate
    // TODO: if you do share a module builder I don't think it's thread-safe
    //       so this method will need sufficient locking/synchronization
    var dynamicAssemblyName = new AssemblyName { Name = $"{Guid.NewGuid()}" };
    var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
    var module = asm.DefineDynamicModule(dynamicAssemblyName.Name);

    // Create a class with a static field to hold our compiled expression
    var typeBuilder = module.DefineType(
        $"{Guid.NewGuid()}",
        TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Serializable);

    var compiledExpressionField = typeBuilder.DefineField(
        "CompiledExpression",
        typeof(Func<T, double>),
        FieldAttributes.Static | FieldAttributes.Private);

    var holderType = typeBuilder.CreateType();

    var compiledExpression = expression.Compile();

    // Get the actual field after we've compiled the type
    var compiledExpressionFieldInfo = holderType.GetField(
        compiledExpressionField.Name,
        BindingFlags.Static | BindingFlags.NonPublic);

    // Store the compiled expression in the static field
    compiledExpressionFieldInfo.SetValue(null, compiledExpression);

    var newDelegate = new DynamicMethod($"{Guid.NewGuid()}",
        typeof(IOrderedEnumerable<T>),
        new[] { typeof(IEnumerable<T>) },
        typeof(ILGen), true);

    var il = newDelegate.GetILGenerator();

    // Load the array passed into the Delegate (T[])
    il.Emit(OpCodes.Ldarg_0);
    // Load the compiled expression from a static field
    il.Emit(OpCodes.Ldsfld, compiledExpressionFieldInfo);
    // Call .OrderByDescending()
    il.Emit(OpCodes.Call, orderByDescMethod);
    // Call .ToArray()
    il.Emit(OpCodes.Call, toArrayMethod);
    il.Emit(OpCodes.Ret); // Stores the sorted array

    return (FilterDelegate<T>)newDelegate.CreateDelegate(typeof(FilterDelegate<T>));
}
5
задан James A Mohler 29 November 2012 в 20:40
поделиться

4 ответа

Трудная задача. Я мог вообразить решение с вложенным циклом на основе GetColumnNames(), использование QueryAddRow() и QuerySetCell(). Это не будет самое эффективное, но это не действительно медленно. Зависит от размера задачи, конечно.

Ваш "создают функцию, которая объединяется, два recordsets" могли быть сделаны намного более эффективными при создании ее для принятия, скажем, десяти аргументов. Измените SQL на лету:

<cfset var local = StructNew()>

<cfquery name="local.union" dbtype="query">
  SELECT * FROM argument1
  <cfloop from="2" to="#ArrayLen(arguments)#" index="local.i">
    <cfif IsQuery(arguments[local.i])>
      UNION
      SELECT * FROM argument#local.i#
    </cfif>
  </cfloop>
</cfquery>

<cfreturn local.union>
1
ответ дан 14 December 2019 в 19:28
поделиться

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

1
ответ дан 14 December 2019 в 19:28
поделиться

После регистрации вопроса я предложил пару решений, но там мог бы быть лучший

  • Я мог записать динамично названные переменные в объем аргументов и затем сослаться на них без их объема в запросе

  • Создайте функцию, которая принимает 2 recordsets как аргументы и возвращает тот объединенный recordset. Это могло быть циклично выполнено для прогрессивного добавления recordset за один раз. Я уверен, что это очень неэффективно по сравнению с выполнением всех ОБЪЕДИНЕНИЙ в одном запросе все же.

2
ответ дан 14 December 2019 в 19:28
поделиться

После быстрого бита ввода по абсолютному адресу вокруг, я нашел это: queryConcat по CFLib.org. Это использует queryaddrow/querysetcell для конкатенации двух запросов.

Я добавил быструю функцию (без проверки ошибок или подтверждения правильности данных, таким образом, я не использую ее как есть):

<cffunction name="concatenate">
     <cfset var result = arguments[1]>
     <cfloop from="2" to="#arraylen(arguments)#" index="i">
             <cfset result=queryconcat(result, arguments[i])>
     </cfloop>
     <cfreturn result>
 </cffunction>

Как тест, я бросил это вместе:

Который действительно, на самом деле, дает Вам fred/sammy/fred.

Это - вероятно, не наиболее эффективное внедрение, но можно всегда изменять код вставки/объединения для создания его быстрее, если Вы хотели. Главным образом я стремился писать как можно меньше код один.:-)

1
ответ дан 14 December 2019 в 19:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: