Bagaimana cara men-cache kueri LINQ yang dikompilasi (bukan hasil)?

Sepertinya setiap kali saya menanyakan sesuatu dengan LINQ NHibernate membuat kueri itu dari awal:

laporan dotTrace

Kodenya terlihat seperti

session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();

Apakah mungkin untuk menghindari kompilasi ulang?

Juga pertanyaan yang sama tentang caching kueri QueryOver/Kriteria (tidak terlalu penting tetapi mungkin masih sesuai dengan cakupannya).


person Vlad    schedule 07.08.2017    source sumber
comment
@stybl menggunakan kembali kueri LINQ dan menggunakan kembali LINQ ke database kueri NH bukanlah hal yang sama   -  person Vlad    schedule 08.08.2017
comment
Saya sadar, itulah sebabnya saya mencabut suara yang ditutup. Tapi lupa tentang komentarnya.   -  person stybl    schedule 08.08.2017
comment
stackoverflow.com/a/4817010/1162077   -  person David Osborne    schedule 08.08.2017


Jawaban (2)


Saya ingat bahwa ekspresi dengan metode berisi linq tidak dikompilasi karena enumerable bisa berbeda di panggilan mendatang

Solusi yang mungkin adalah dengan menggunakan operator OR yang jelek jika Anda tahu bahwa jumlah elemen selalu sama

Solusi lain adalah dengan memanggil metode berikut:

sesi.QueryOver().AndRestrictionOn(x=>x.id).IsIn(ids)

person Molem    schedule 14.06.2020

Khususnya kasus ini disebabkan oleh akses ke id (int[]) di sini

session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();

diubah menjadi MemberAccessExpression (bukan ConstantExpression) dan NHibernate harus mengevaluasinya. Meskipun ids tidak pernah diubah, ia masih dimasukkan ke dalam kelas yang dihasilkan penutupan (seperti DisplayClass<>.ids).

Saya mengoptimalkan kasus ini dengan membuat PartialEvaluatingExpressionTreeVisitor versi saya sendiri:

    protected Expression EvaluateSubtree(Expression subtree)
    {
        ArgumentUtility.CheckNotNull(nameof(subtree), subtree);
        var memberExpression = subtree as MemberExpression;
        if (memberExpression != null)
        {
            Expression constant;
            if (TryEvaluateMember(memberExpression, out constant)) return constant;
        }

        if (subtree.NodeType != ExpressionType.Constant)
            throw new NHibernateExpressionOptimizerException(subtree);
        ConstantExpression constantExpression = (ConstantExpression)subtree;
        IQueryable queryable = constantExpression.Value as IQueryable;
        if (queryable != null && queryable.Expression != constantExpression)
            return queryable.Expression;
        return constantExpression;
    }

    bool TryEvaluateMember(MemberExpression memberExpression, out Expression constant)
    {
        constant = null;
        ConstantExpression c = memberExpression.Expression == null ? Expression.Constant(null) : EvaluateSubtree(memberExpression.Expression) as ConstantExpression;
        if (c == null) return false;
        var fieldInfo = memberExpression.Member as FieldInfo;
        if (fieldInfo != null)
        {
            constant = Expression.Constant(ReflectorReadFieldDelegate(fieldInfo, c.Value));
            return true;
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;
        if (propertyInfo != null)
        {
            constant = Expression.Constant(ReflectorReadPropertyDelegate(propertyInfo, c.Value));
            return true;
        }
        return false;
    }

Delegasi reflektor menggunakan semacam sihir refleksi.emit yang di-cache.

person Vlad    schedule 08.08.2017
comment
Ini sepertinya mengubah perilaku default, yang secara umum tidak saya sukai. Mengapa tidak menggabungkannya dalam pemanggilan metode, seperti public static T ToConst‹T›(T item) { return item;}, lalu menerapkan logika Anda untuk pemanggilan metode tersebut? ini memberi Anda lebih banyak fleksibilitas, dan lebih jelas apa niat Anda, bukan? - person MBoros; 12.08.2017