Entity Framework Core IQueryable <T> 'den SQL kodu alın


92

Entity Framework Core kullanıyorum ve hangi SQL kodunun oluşturulduğunu görmem gerekiyor. Entity Framework'ün önceki sürümlerinde aşağıdakileri kullanabilirdim:

string sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();

Sorgu bir IQueryable nesnesidir ... Ancak ToTraceString EF Core'da kullanılamaz.

EF Core'da benzer bir şeyi nasıl yapabilirim?



Bunu deneyebilirsiniz: rion.io/2016/10/19/… .
mikebridge

Yanıtlar:


82

EF çekirdek 5 / Net 5

query.ToQueryString()EF Core 5.0'daki Yeniliklere Bakın

var query = _context.Widgets.Where(w => w.IsReal && w.Id == 42);  
var sql = query.ToQueryString();

Daha eski ağ çekirdek çerçeveleri için bir Uzantı kullanılabilir.

Çekirdek 2.1.2


using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.Sql;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;

    public static class QueryableExtensions
    {
        private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
    
        private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");
        private static readonly FieldInfo QueryModelGeneratorField = typeof(QueryCompiler).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryModelGenerator");
        private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
        private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");
    
        public static string ToSql<TEntity>(this IQueryable<TEntity> query)
        {
            var queryCompiler = (QueryCompiler) QueryCompilerField.GetValue(query.Provider);
            var queryModelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
            var queryModel = queryModelGenerator.ParseQuery(query.Expression);
            var database = DataBaseField.GetValue(queryCompiler);
            var databaseDependencies = (DatabaseDependencies) DatabaseDependenciesField.GetValue(database);
            var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
            var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
            modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
            var sql = modelVisitor.Queries.First().ToString();
    
            return sql;
        }
    }

EF Çekirdek 3.0

        public static string ToSql<TEntity>(this IQueryable<TEntity> query)
        {
            var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
            var enumeratorType = enumerator.GetType();
            var selectFieldInfo = enumeratorType.GetField("_selectExpression", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"cannot find field _selectExpression on type {enumeratorType.Name}");
            var sqlGeneratorFieldInfo = enumeratorType.GetField("_querySqlGeneratorFactory", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"cannot find field _querySqlGeneratorFactory on type {enumeratorType.Name}");
            var selectExpression = selectFieldInfo.GetValue(enumerator) as SelectExpression ?? throw new InvalidOperationException($"could not get SelectExpression");
            var factory = sqlGeneratorFieldInfo.GetValue(enumerator) as IQuerySqlGeneratorFactory ?? throw new InvalidOperationException($"could not get IQuerySqlGeneratorFactory");
            var sqlGenerator = factory.Create();
            var command = sqlGenerator.GetCommand(selectExpression);
            var sql = command.CommandText;
            return sql;
        }

RosiOli'den Öz bkz.

EF Core 3.1

using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Query;

public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
{
    var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
    var relationalCommandCache = enumerator.Private("_relationalCommandCache");
    var selectExpression = relationalCommandCache.Private<SelectExpression>("_selectExpression");
    var factory = relationalCommandCache.Private<IQuerySqlGeneratorFactory>("_querySqlGeneratorFactory");

    var sqlGenerator = factory.Create();
    var command = sqlGenerator.GetCommand(selectExpression);

    string sql = command.CommandText;
    return sql;
}

private static object Private(this object obj, string privateField) => obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
private static T Private<T>(this object obj, string privateField) => (T)obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);

Sorun ayrıca EF net çekirdek ekibi tarafından izleniyor ve bir sonraki sürüm için planlanıyor.


1
Bunun bir IQueryableile değil, bir ile çalışmak için nasıl yazılması gerektiğine dair bir örnek verebilir misiniz IQueryable<T>?
byrnedo

Sanırım her zaman bir IQueryable<T>. widgetYukarıdaki örneğe bakın . Yalnızca IQueryable olan bir örneğiniz var mı?
Thom Kiesewetter

Ben kullanıyorum github.com/StefH/System.Linq.Dynamic.Core bir verir, IQueryablesadece
byrnedo

Çerçevenizde sorgularınız <T> enitity tipine dayalıdır. ToSql bir enityType'a ihtiyaç duyar çünkü bir sql ifadesi oluşturmak için alanı ve tablo adını bilmesi gerekir. Bu bilgi olmadan yapılamaz.
Thom Kiesewetter

1
var relationalCommandCache = enumerator.Private ("_ relationalCommandCache"); null döndürür
Khurram Ali

81

Bu yanıt EF Core 2.1 içindir. EF Core 3.0 ve 3.1 için @Thom Kiesewetter'ın cevabına bakın

EF Core 5 için, ToQueryString()üzerinde kullanılan yerleşik bir yöntem olacaktır .IQueryable<>

EF 7, Entity Framework Core olarak yeniden adlandırıldığından, size EF Core seçeneklerini özetleyeceğim.

Aşağıdakilerden SQL ifadelerini günlüğe kaydetmek için 3 yaklaşım vardır IQueryable<>:

  • Yerleşik veya Özel Günlüğü Kullanma . Bu öğreticide belirtildiği gibi, seçtiğiniz günlükçüyü veya .NET Core'daki yerleşik Logger'ı kullanarak yürütme sorgusunu günlüğe kaydetme .
  • Profil Oluşturucu Kullanma . Yürütülen sorguyu izlemek için MiniProfiler gibi bir SQL Profiler kullanma .
  • Çılgın Yansıma Kodunu Kullanma . Aynı temel konsepti gerçekleştirmek için eski yaklaşıma benzer bazı özel yansıma kodu uygulayabilirsiniz.

İşte çılgın yansıma kodu (genişletme yöntemi):

public static class IQueryableExtensions
{
    private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

    private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");

    private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");

    private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");

    private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");

    public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
    {
        var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
        var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
        var queryModel = modelGenerator.ParseQuery(query.Expression);
        var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
        var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
        var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
        var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();
        modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
        var sql = modelVisitor.Queries.First().ToString();

        return sql;
    }
}

Bu uzantı yöntemini kodunuza ekledikten sonra aşağıdaki yöntemi kullanabilirsiniz:

// Build a query using Entity Framework
var query = _context.Widgets.Where(w => w.IsReal && w.Id == 42);  
// Get the generated SQL
var sql = query.ToSql();  

Yönlendirme: http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/ ve https://gist.github.com / rionmonster / 2c59f449e67edf8cd6164e9fe66c545a


1
Yorumlarınız için teşekkürler. Kodu şimdi 2.1 ile çalışacak şekilde güncelledim.
Nikolay Kostov

1
@SteffenMangold hata ayıklama amaçlıdır :) Hızlı olması amaçlanmamıştır.
Nikolay Kostov

1
@RicardoPeres: hayır, gönderinize kredi veren rion.io/2016/10/19/… referansı var .
Martijn Pieters

1
@Alexei Kullanmaya başladım optionsBuilder.UseLoggerFactory(LoggerFactory); public static readonly LoggerFactory LoggerFactory = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) });çünkü daha güzel bir sql oluşturuyor, ama ne yazık ki çok fazla spam de oluşturuyor.
Joelty

2
.Net Core 3.0 ve EF Core 3.0 artık GA'da yayınlandı ve yöntemle ilgili son değişiklikleri var: ToSql. 3.0 için nasıl yeniden uygulanacağı hakkında bir fikriniz var mı? Daha fazla bilgi: github.com/aspnet/EntityFrameworkCore/issues/18029
borisdj

40

Tek seferlik yanlış çalışan bir EF Core sorgusunu veya benzerini teşhis etmeye çalışan ve kodunu değiştirmek istemeyen herkes için birkaç seçenek vardır:

SQL Server Management Studio (SSMS) SQL Profiler'ı kullanın

SQL Server Management Studio (SSMS) kuruluysa , SSMS'deki Araçlar menüsünden SQL Profiler'ı çalıştırabilirsiniz :

SQL Server Management Studio'daki (SSMS) Araçlar menüsündeki SQL Profiler seçeneği

Ve sonra açıldığında SQL Profiler'da çalışan yeni bir izlemeye başlayın.

Daha sonra EF'ten gelen SQL isteğini görebileceksiniz, bunlar genellikle oldukça iyi biçimlendirilmiş ve okunması kolaydır.

Visual Studio'da Çıktı Penceresini Kontrol Edin

VS2019 kopyamda, EF2.2 kullanarak çıktı penceresini Web Sunucusundan çıktıyı gösterecek şekilde değiştirebilirim (Çıktı bölmesinin üstündeki "Çıktıyı göster" kombinasyonunda uygulamanızın ve web sunucunuzun adını seçin) ve giden SQL de orada gösterilir. Kodumu kontrol ettim ve görebildiğim kadarıyla bunu etkinleştirmek için hiçbir şey yapmadım, bu yüzden bunu varsayılan olarak yapması gerektiğini düşünüyorum:

görüntü açıklamasını buraya girin

Sorgularda SQL sunucusuna gönderilen parametreleri görmek istiyorsanız, DBContext'i EnableSensitiveDataLoggingyöntemle kurarken bunu açabilirsiniz , örn.

services.AddDbContext<FusionContext>(options => options
    .UseSqlServer(connectionString))
    //.EnableDetailedErrors()
    .EnableSensitiveDataLogging()

@Tich - Lil3p yorumlarda , projenin Özellikler sayfasının Hata Ayıklama sekmesinde SQL Hata Ayıklamayı açmak için bir anahtar kullanmaları gerektiğinden bahseder (bu "sqlDebugging": true, LaunchSettings.json'da ayarlanır). Kontrol ettim ve projelerimden herhangi biri için bunu açmadım, ancak yukarıdakiler sizin için çalışmıyorsa bu da denemeye değer olabilir.


3
Azure Sql için bir seçenek değil
Emil

@batmaci Azure için çalışabilecek başka bir yöntem ekledim
tomRedox

Çıktıyı EF Core'dan alıyorum, ancak bana @__ p_0, vb. İçin kullandığı değişkenleri göstermiyor
DaleyKD

@DaleyKD bellek bana doğru hizmet veriyorsa bu bir güvenlik sorunudur - MVC'nin varsayılan olarak parametreleri gizlediğini düşünüyorum çünkü hassas verileri içerebiliyorlar. MVC için hata ayıklama seçeneklerinden birinin parametrelerin gösterilmesine neden olacağını düşünüyorum, ancak hangisinin olduğunu hatırlayamıyorum. app.UseDeveloperExceptionPage()Koduma baktığımda Startup.Configure ve services.AddServerSideBlazor() .AddCircuitOptions(options => { options.DetailedErrors = true; });Startup.ConfigureServices içinde var. Bunlardan biri, gösterilen parametreleri alabilir.
tomRedox

1
Bu bağlantı bana yardımcı oldu -> thecodebuzz.com/adding-logging-in-entity-framework-core .
Yuri Cardoso

3

Benim aldığım @ nikolay-kostov cevabına dayanıyor.

Aradaki fark, SQL komutunu sabit kodlanmış yerine ayıklanmış parametrelerle almam, bu da EF Core'un veritabanına komut gönderme şekliyle daha uyumludur. Ayrıca, komutu veritabanına düzenlemek ve göndermek istiyorsanız, parametreleri kullanmak daha iyi bir uygulamadır.

    private static class IQueryableUtils 
    {
        private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

        private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");

        private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");
        private static readonly FieldInfo queryContextFactoryField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryContextFactory");
        private static readonly FieldInfo loggerField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_logger");
        private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");

        private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");

        public static (string sql, IReadOnlyDictionary<string, object> parameters) ToSql<TEntity>(IQueryable<TEntity> query) where TEntity : class
        {
            var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
            var queryContextFactory = (IQueryContextFactory)queryContextFactoryField.GetValue(queryCompiler);
            var logger = (Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger<DbLoggerCategory.Query>)loggerField.GetValue(queryCompiler);
            var queryContext = queryContextFactory.Create();
            var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
            var newQueryExpression = modelGenerator.ExtractParameters(logger, query.Expression, queryContext);
            var queryModel = modelGenerator.ParseQuery(newQueryExpression);
            var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
            var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
            var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
            var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();

            modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
            var command = modelVisitor.Queries.First().CreateDefaultQuerySqlGenerator()
                .GenerateSql(queryContext.ParameterValues);

            return (command.CommandText, queryContext.ParameterValues);
        }
    }



2

Entity Framework Core 3.x

Günlüğe kaydetme yoluyla elde edebilirsiniz.

Fabrikayı oluşturun:

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
    .AddConsole((options) => { })
    .AddFilter((category, level) =>
        category == DbLoggerCategory.Database.Command.Name
        && level == LogLevel.Information);
});

DbContextHangi fabrikanın kullanılacağını söyleyin :

optionsBuilder.UseLoggerFactory(_loggerFactory);

Gönderen bu yazı

ILogger'ı uygulamak istiyorsanız daha fazla bilgi edinebilirsiniz:

public class EntityFrameworkSqlLogger : ILogger
{
    #region Fields
    Action<EntityFrameworkSqlLogMessage> _logMessage;
    #endregion
    #region Constructor
    public EntityFrameworkSqlLogger(Action<EntityFrameworkSqlLogMessage> logMessage)
    {
        _logMessage = logMessage;
    }
    #endregion
    #region Implementation
    public IDisposable BeginScope<TState>(TState state)
    {
        return default;
    }
    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (eventId.Id != 20101)
        {
            //Filter messages that aren't relevant.
            //There may be other types of messages that are relevant for other database platforms...
            return;
        }
        if (state is IReadOnlyList<KeyValuePair<string, object>> keyValuePairList)
        {
            var entityFrameworkSqlLogMessage = new EntityFrameworkSqlLogMessage
            (
                eventId,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "commandText").Value,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "parameters").Value,
                (CommandType)keyValuePairList.FirstOrDefault(k => k.Key == "commandType").Value,
                (int)keyValuePairList.FirstOrDefault(k => k.Key == "commandTimeout").Value,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "elapsed").Value
            );
            _logMessage(entityFrameworkSqlLogMessage);
        }
    }
    #endregion
}

1

Değişkenler EF Çekirdek 3.1 için (bazı göre şu var halllo gelen GitHub yorumlar gr'dan açıklama, yukarıda verilmiştir) Thom Kiesewetter ve diğ.

/// <summary>
/// SQL Extension methods to get the SQL and check correctness
/// Class can be removed with EF Core 5 (https://github.com/dotnet/efcore/issues/6482#issuecomment-587605366) (although maybe variable substitution might still be necessary if we want them inline)
/// </summary>
public static class SqlExtensions
{
    private static object Private(this object obj, string privateField) => obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
    private static T Private<T>(this object obj, string privateField) => (T)obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);

    /// <summary>
    /// Gets a SQL statement from an IQueryable
    /// </summary>
    /// <param name="query">The query to get the SQL statement for</param>
    /// <returns>Formatted SQL statement as a string</returns>
    public static string ToQueryString<TEntity>(this IQueryable<TEntity> query) where TEntity : class
    {
        using var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
        var relationalCommandCache = enumerator.Private("_relationalCommandCache");
        var selectExpression = relationalCommandCache.Private<SelectExpression>("_selectExpression");
        var factory = relationalCommandCache.Private<IQuerySqlGeneratorFactory>("_querySqlGeneratorFactory");
        var relationalQueryContext = enumerator.Private<RelationalQueryContext>("_relationalQueryContext");

        var sqlGenerator = factory.Create();
        var command = sqlGenerator.GetCommand(selectExpression);
        var parametersDict = relationalQueryContext.ParameterValues;

        return SubstituteVariables(command.CommandText, parametersDict);
    }

    private static string SubstituteVariables(string commandText, IReadOnlyDictionary<string, object> parametersDictionary)
    {
        var sql = commandText;
        foreach (var (key, value) in parametersDictionary)
        {
            var placeHolder = "@" + key;
            var actualValue = GetActualValue(value);
            sql = sql.Replace(placeHolder, actualValue);
        }

        return sql;
    }

    private static string GetActualValue(object value)
    {
        var type = value.GetType();

        if (type.IsNumeric())
            return value.ToString();

        if (type == typeof(DateTime) || type == typeof(DateTimeOffset))
        {
            switch (type.Name)
            {
                case nameof(DateTime):
                    return $"'{(DateTime)value:u}'";

                case nameof(DateTimeOffset):
                    return $"'{(DateTimeOffset)value:u}'";
            }
        }

        return $"'{value}'";
    }

    private static bool IsNullable(this Type type)
    {
        return
            type != null &&
            type.IsGenericType &&
            type.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

    private static bool IsNumeric(this Type type)
    {
        if (IsNullable(type))
            type = Nullable.GetUnderlyingType(type);

        if (type == null || type.IsEnum)
            return false;

        return Type.GetTypeCode(type) switch
        {
            TypeCode.Byte => true,
            TypeCode.Decimal => true,
            TypeCode.Double => true,
            TypeCode.Int16 => true,
            TypeCode.Int32 => true,
            TypeCode.Int64 => true,
            TypeCode.SByte => true,
            TypeCode.Single => true,
            TypeCode.UInt16 => true,
            TypeCode.UInt32 => true,
            TypeCode.UInt64 => true,
            _ => false
        };
    }
}

Bu belki tüm türleri ikame etmez, ancak çoğu kapsanmıştır. Uzatmaktan çekinmeyin.


0

Bir kamu hizmeti olarak:

    var someQuery = (
        from projects in _context.projects
        join issues in _context.issues on projects.Id equals issues.ProjectId into tmpMapp
        from issues in tmpMapp.DefaultIfEmpty()
        select issues
    ) //.ToList()
    ;

    // string sql = someQuery.ToString();
    // string sql = Microsoft.EntityFrameworkCore.IQueryableExtensions.ToSql(someQuery);
    // string sql = Microsoft.EntityFrameworkCore.IQueryableExtensions1.ToSql(someQuery);
    // using Microsoft.EntityFrameworkCore;
    string sql = someQuery.ToSql();
    System.Console.WriteLine(sql);

Ve sonra bu uzantı yöntemleri (.NET Core 1.0 için IQueryableExtensions1, .NET Core 2.0 için IQueryableExtensions):

    using System;
    using System.Linq;
    using System.Reflection;
    using Microsoft.EntityFrameworkCore.Internal;
    using Microsoft.EntityFrameworkCore.Query;
    using Microsoft.EntityFrameworkCore.Query.Internal;
    using Microsoft.EntityFrameworkCore.Storage;
    using Remotion.Linq.Parsing.Structure;


    namespace Microsoft.EntityFrameworkCore
    {

        // /programming/1412863/how-do-i-view-the-sql-generated-by-the-entity-framework
        // http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/

        public static class IQueryableExtensions
        {
            private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

            private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields
                .First(x => x.Name == "_queryCompiler");

            private static readonly PropertyInfo NodeTypeProviderField =
                QueryCompilerTypeInfo.DeclaredProperties.Single(x => x.Name == "NodeTypeProvider");

            private static readonly MethodInfo CreateQueryParserMethod =
                QueryCompilerTypeInfo.DeclaredMethods.First(x => x.Name == "CreateQueryParser");

            private static readonly FieldInfo DataBaseField =
                QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");

            private static readonly PropertyInfo DatabaseDependenciesField =
                typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");

            public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
            {
                if (!(query is EntityQueryable<TEntity>) && !(query is InternalDbSet<TEntity>))
                {
                    throw new ArgumentException("Invalid query");
                }

                var queryCompiler = (QueryCompiler) QueryCompilerField.GetValue(query.Provider);
                var nodeTypeProvider = (INodeTypeProvider) NodeTypeProviderField.GetValue(queryCompiler);
                var parser = (IQueryParser) CreateQueryParserMethod.Invoke(queryCompiler, new object[] {nodeTypeProvider});
                var queryModel = parser.GetParsedQuery(query.Expression);
                var database = DataBaseField.GetValue(queryCompiler);
                var databaseDependencies = (DatabaseDependencies) DatabaseDependenciesField.GetValue(database);
                var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
                var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
                modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
                var sql = modelVisitor.Queries.First().ToString();

                return sql;
            }
        }



        public class IQueryableExtensions1
        {
            private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

            private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo()
                .DeclaredFields
                .First(x => x.Name == "_queryCompiler");

            private static readonly PropertyInfo NodeTypeProviderField =
                QueryCompilerTypeInfo.DeclaredProperties.Single(x => x.Name == "NodeTypeProvider");

            private static readonly MethodInfo CreateQueryParserMethod =
                QueryCompilerTypeInfo.DeclaredMethods.First(x => x.Name == "CreateQueryParser");

            private static readonly FieldInfo DataBaseField =
                QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");

            private static readonly FieldInfo QueryCompilationContextFactoryField = typeof(Database).GetTypeInfo()
                .DeclaredFields.Single(x => x.Name == "_queryCompilationContextFactory");


            public static string ToSql<TEntity>(IQueryable<TEntity> query) where TEntity : class
            {
                if (!(query is EntityQueryable<TEntity>) && !(query is InternalDbSet<TEntity>))
                {
                    throw new ArgumentException("Invalid query");
                }

                var queryCompiler = (IQueryCompiler) QueryCompilerField.GetValue(query.Provider);

                var nodeTypeProvider = (INodeTypeProvider) NodeTypeProviderField.GetValue(queryCompiler);
                var parser =
                    (IQueryParser) CreateQueryParserMethod.Invoke(queryCompiler, new object[] {nodeTypeProvider});
                var queryModel = parser.GetParsedQuery(query.Expression);
                var database = DataBaseField.GetValue(queryCompiler);
                var queryCompilationContextFactory =
                    (IQueryCompilationContextFactory) QueryCompilationContextFactoryField.GetValue(database);
                var queryCompilationContext = queryCompilationContextFactory.Create(false);
                var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
                modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
                var sql = modelVisitor.Queries.First().ToString();

                return sql;
            }


        }


    }

En son EF Core 2.1.1 ile bu artık çalışmıyor. Özel statik salt okunurdur PropertyInfo NodeTypeProviderField = QueryCompilerTypeInfo.DeclaredProperties.Single (x => x.Name == "NodeTypeProvider");
Stef Heyenrath

@Stef Heyenrath: Sanırım cevabım net bir şekilde .NET Core 1.0 ve 2.0'ı gösteriyor, 2.1 veya 2.2'yi değil. Diğerleri zaten 2.2, 3.0 ve 3.1 kodunu verdiler. Bu cevabı yazdığım sırada .NET Core 2.1 yayınlanmadı. NET Core 2.0 ve 1.0 için mükemmel bir şekilde geçerlidir
Stefan Steiger

0

EF Core 3 ve üzeri için, EFCore.BulkExtensions bir ToParametrizedSql yöntemine sahiptir. Tek yakınmam, parametreleri Microsoft.Data.SqlClient olarak döndürmesidir, bu nedenle bazen bağlantı türümse onları System.Data.SqlClient'e dönüştürmem gerekir.

https://github.com/borisdj/EFCore.BulkExtensions

EFCore.BulkExtensions.IQueryableExtensions.ToParametrizedSql
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.