Как создать Дерево выражений LINQ для выбора анонимного типа

root@i5-cpu:~/work_area/php# tree d1

    d1
    ├── d2
    │   ├── f2_1.js
    │   └── f2_2.js
    ├── f1_1.js
    └── f1_2.js

1 directory, 4 files

root@i5-cpu:~/work_area/php# find . -name "\*.js" | xargs tar -cvf tar.tar --transform='s,.*/,,'

./d1/d2/f2_1.js
./d1/d2/f2_2.js
./d1/f1_1.js
./d1/f1_2.js

root@i5-cpu:~/work_area/php# tar -tvf tar.tar

-rw-rw-rw- root/root         0 2019-01-17 01:12 f2_1.js
-rw-rw-rw- root/root         0 2019-01-17 01:12 f2_2.js
-rw-rw-rw- root/root         0 2019-01-17 01:11 f1_1.js
-rw-rw-rw- root/root         0 2019-01-17 01:11 f1_2.js

Проверить эту страницу https://www.gnu.org/software/tar/manual/html_section/tar_51.html Примеры с - преобразование [112 ]

45
задан abatishchev 5 March 2015 в 20:59
поделиться

6 ответов

Это может быть сделано, как упомянуто, с помощью Отражения Испускают и класс помощника, который я включал ниже. Код ниже является происходящей работой, поэтому возьмите его если это имеет значение..., 'он работает над моим полем'. Класс метода SelectDynamic должен быть брошен в статическом дополнительном классе метода.

Как ожидалось Вы не получите Intellisense, так как тип не создается до времени выполнения. Работы, хорошие на последних ограниченных средствах управления данными.

public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<string> fieldNames)
{
    Dictionary<string, PropertyInfo> sourceProperties = fieldNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name));
    Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);

    ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t");
    IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();

    Expression selector = Expression.Lambda(Expression.MemberInit(
        Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

    return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType },
                 Expression.Constant(source), selector));
}



public static class LinqRuntimeTypeBuilder
{
    private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    private static AssemblyName assemblyName = new AssemblyName() { Name = "DynamicLinqTypes" };
    private static ModuleBuilder moduleBuilder = null;
    private static Dictionary<string, Type> builtTypes = new Dictionary<string, Type>();

    static LinqRuntimeTypeBuilder()
    {
        moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name);
    }

    private static string GetTypeKey(Dictionary<string, Type> fields)
    {
        //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter
        string key = string.Empty;
        foreach (var field in fields)
            key += field.Key + ";" + field.Value.Name + ";";

        return key;
    }

    public static Type GetDynamicType(Dictionary<string, Type> fields)
    {
        if (null == fields)
            throw new ArgumentNullException("fields");
        if (0 == fields.Count)
            throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition");

        try
        {
            Monitor.Enter(builtTypes);
            string className = GetTypeKey(fields);

            if (builtTypes.ContainsKey(className))
                return builtTypes[className];

            TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

            foreach (var field in fields)                    
                typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public);

            builtTypes[className] = typeBuilder.CreateType();

            return builtTypes[className];
        }
        catch (Exception ex)
        {
            log.Error(ex);
        }
        finally
        {
            Monitor.Exit(builtTypes);
        }

        return null;
    }


    private static string GetTypeKey(IEnumerable<PropertyInfo> fields)
    {
        return GetTypeKey(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }

    public static Type GetDynamicType(IEnumerable<PropertyInfo> fields)
    {
        return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }
}
70
ответ дан Beakie 26 November 2019 в 21:13
поделиться

Я не полагаю, что Вы будете в состоянии достигнуть этого. Хотя, когда Вы делаете select new { c.Name, c.Population }, кажется, что Вы не создаете класс, Вы на самом деле. Если Вы взглянете на скомпилированный вывод в Отражателе или необработанном IL, то Вы будете в состоянии видеть это.

у Вас будет класс, который выглядел бы примерно так:

[CompilerGenerated]
private class <>c__Class {
  public string Name { get; set; }
  public int Population { get; set; }
}

(Хорошо, я убрал его касание, так как свойство действительно просто get_Name() и set_Name(name) набор метода так или иначе)

, Что Вы пытаетесь сделать, надлежащее динамическое создание класса, что-то, что привычка быть доступной, пока.NET 4.0 не выходит (и даже тогда я не действительно уверен, будет ли это в состоянии достигнуть того, что Вы хотите).

Вы - лучшее решение, должен был бы определить различное анонимный классы и затем иметь некоторую логическую проверку для определения, какой создать и создать его можно использовать объект System.Linq.Expressions.NewExpression.

, Но, может быть (в теории, по крайней мере) возможно сделать это, если Вы получаете действительно хардкор о базовом поставщике LINQ. Если Вы запись Вашего собственного поставщика LINQ, можно обнаружить, если проанализированным в настоящее время выражением является Выбор, то Вы определяете CompilerGenerated класс, отражаетесь для его конструктора и создаете.

Вызывающе не простая задача, но это было бы, как LINQ к SQL, LINQ к XML, и т.д. все делают это.

1
ответ дан Aaron Powell 26 November 2019 в 21:13
поделиться

Вы могли использовать класс параметра вместо того, чтобы работать с анонимным типом. В Вашем примере можно создать класс параметра как это:

public struct ParamClass {
    public string Name { get; set; };
    public int Population { get; set; };
}

†¦ и помещенный это в Ваш выбор как это:

var v = from c in Countries
        where c.City == "London"
        select new ParamClass {c.Name, c.Population};

то, Что Вы вынимаете, является чем-то вроде типа IQueryable<ParamClass>.

1
ответ дан Spoike 26 November 2019 в 21:13
поделиться

Это компилирует, я не знаю, работает ли это однако...

myEnumerable.Select((p) => { return new { Name = p.Name, Description = p.Description }; });

Принятие p - то, что Ваше преобразование и избранный оператор возвращают скоро тип, с помощью объявления функции лямбды.

Редактирование: Я также не знаю, как Вы генерировали бы это динамично. Но по крайней мере это показывает Вам, как использовать избранную лямбду для возврата скоро тип с несколькими значениями

Edit2:

Вы также имели бы к пустому в памяти, что c# компилятор на самом деле генерирует статические классы скоро тип. Так скоро тип действительно на самом деле имеет тип после времени компиляции. Таким образом, если Ваша генерация этих запросов во время выполнения (который я принимаю Вас) Вам, вероятно, придется создать тип с помощью различных отражательных методов (я полагаю, что можно использовать их для создания типов на лету), загружают созданные типы в контекст выполнения и используют их в сгенерированном выводе.

1
ответ дан Sekhat 26 November 2019 в 21:13
поделиться

Я думаю, что большинству вещей уже отвечают - как Slace заявил, Вам нужен некоторый класс, который был бы возвращен из Select метод. Как только у Вас есть класс, можно использовать System.Linq.Expressions.NewExpression метод для создания выражения.

, Если Вы действительно хотите сделать это, можно генерировать класс во времени выполнения также. Это - немного больше работы, потому что это не может быть сделано с помощью Деревьев выражений LINQ, но это возможно. Можно использовать System.Reflection.Emit пространство имен, чтобы сделать это - я просто сделал быстрый поиск и здесь являюсь статьей, которая объясняет это:

1
ответ дан Tomas Petricek 26 November 2019 в 21:13
поделиться

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

 Select("new(<property1>,<property2>,...)");

Вам нужен файл Dynamics.cs от LINQ и образцов языка для Visual Studio для этого для работы, оба связаны у основания эта страница . Можно также видеть, что рабочий пример показывает это в действии с в том же URL.

0
ответ дан Adrian Grigore 26 November 2019 в 21:13
поделиться
Другие вопросы по тегам:

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