【问题标题】:Obtain ToTraceString() for IQueryable获取 IQueryable 的 ToTraceString()
【发布时间】:2013-10-30 01:09:52
【问题描述】:

我用

  `.....
   .....
  query = query.Where(criterion, fiedNames);
  string sql2 = ((ObjectQuery)query).ToTraceString();'

获取和调整将由 LINQ 执行的 SQL 代码。

当我快速观看时,我得到以下我不理解的 SQL 语句。

SELECT [UnionAll2].[C3] AS [C1], [UnionAll2].[C4] AS [C2],
[UnionAll2].[C5] AS [C3], [UnionAll2].[C6] AS [C4], 
[UnionAll2].[C7] AS [C5], 
[UnionAll2].[C8] AS [C6], 
[UnionAll2].[C9] AS [C7],  
..... few hundereds line more.....
@p__linq__4 AS [C2], 
.....
CASE 
     WHEN ( [Join5].[id] IS NULL ) THEN 
     Cast(NULL AS INT) 
     WHEN ( @p__linq__0 = 1 )

.......

显然,如果我粘贴到 SQL 管理工作室并执行,如果我没有传递 @p__linq__0.... 的值,它会抛出错误。

我的问题:如何获取可以在 SQL 管理上执行的跟踪 SQL?

【问题讨论】:

  • 实际上,您的示例对我来说看起来像是有效的 SQL。显然 @p__linq__4 的东西必须在一个 SP 参数中传递,但除此之外,这一切看起来完全合法。 (丑吗?是的,这是您在使用 ORM 时所做的权衡)另外,实际的 SQL 错误是什么?
  • 我在 SQL 中没有收到任何错误,除了我必须传递这些参数(我不知道),所以有没有办法或需要调整才能不生成@p___linq___0?我的 api 在不提供参数的情况下渲染数据,这些参数是可选的。
  • 我要问的是,当您尝试在 SSMS 中执行 SQL 时,您得到了什么错误(即“它会抛出错误”)?
  • 我会更正我在帖子中所说的话,但我想说的是,如果我只是复制并粘贴 sql 含义而不传递参数,它会引发错误。
  • 对!但那是什么错误!? :)

标签: c# linq entity-framework lambda linq-to-entities


【解决方案1】:
   public class LinqProvider<T>
    {
        static readonly FieldInfo INTERNAL_QUERY_FIELD;
        static readonly FieldInfo QUERYSTATE_FIELD;

        static LinqProvider()
        {
            INTERNAL_QUERY_FIELD = typeof(DbQuery<T>).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(f => f.Name.Equals("_internalQuery"));
            QUERYSTATE_FIELD = typeof(System.Data.Entity.Core.Objects.ObjectQuery<>).BaseType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == "_state");
        }

        public IQueryable<T> QueryContext { get; set; }

        InternalQuery _InternalQueryContext;
        public InternalQuery InternalQueryContext
        {
            get
            {
                if (_InternalQueryContext == null)
                {
                    _InternalQueryContext = new InternalQuery();

                    if (QueryContext is DbQuery<T>)
                    {
                        var internalQuery = INTERNAL_QUERY_FIELD.GetValue(QueryContext);

                        var objectQueryField = internalQuery.GetType().GetProperties().FirstOrDefault(f => f.Name.Equals("ObjectQuery"));

                        _InternalQueryContext.ObjectQueryContext = objectQueryField.GetValue(internalQuery) as System.Data.Entity.Core.Objects.ObjectQuery<T>;
                    }
                    else if (QueryContext is System.Data.Entity.Core.Objects.ObjectQuery<T>)
                    {
                        _InternalQueryContext.ObjectQueryContext = (QueryContext as System.Data.Entity.Core.Objects.ObjectQuery<T>);
                    }
                }

                return _InternalQueryContext;
            }
        }

        public LinqProvider(IQueryable<T> queryContext)
        {
            QueryContext = queryContext;
        }

        public class InternalQuery
        {
            public System.Data.Entity.Core.Objects.ObjectQuery<T> ObjectQueryContext { get; set; }

            public string ToTraceString(IDictionary<string, object> Parameters = null)
            {
                var state = QUERYSTATE_FIELD.GetValue(ObjectQueryContext);

                var mi = state.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == "GetExecutionPlan");

                mi.Invoke(state, new object[] { null });

                var paramState = state.GetType().BaseType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == "_parameters");

                if (paramState != null)
                {
                    System.Data.Entity.Core.Objects.ObjectParameterCollection col = paramState.GetValue(state) as System.Data.Entity.Core.Objects.ObjectParameterCollection;

                    if (col != null && Parameters != null)
                    {
                        foreach (var item in col)
                        {
                            Parameters.Add(item.Name, item.Value);
                        }
                    }
                }

                return ObjectQueryContext.ToTraceString();
            }
        }
    }

【讨论】:

    【解决方案2】:

    这个你可以断言的扩展方法:

     /// <summary>
        /// Traces IQueryable's DB query />
        /// </summary>
        /// <typeparam name="TEntity">IQueryable type</typeparam>
        /// <param name="query">The IQueryable query</param>
        /// <returns>Query</returns>
        public static string ToTraceString<TEntity>(this IQueryable<TEntity> query)
        {
            StringBuilder sb = new StringBuilder();
    
            var objQuery = query as ObjectQuery<TEntity>;
            if (objQuery != null)
            {
                sb.AppendLine();
                if(objQuery.Parameters.Count > 0)
                {
                    sb.AppendLine("Query parameters:");
                    sb.AppendLine("--------------------------------------------------------------");
                    sb.AppendLine("      Name          |        Type        |       Value        ");
                    sb.AppendLine("--------------------------------------------------------------");
                    foreach (var param in objQuery.Parameters) 
                    {
                        sb.AppendFormat("{0, -20}|{1, -20}|{2, -20}{3}", param.Name, param.ParameterType.ToString(), param.Value, Environment.NewLine);
                    }
                    sb.AppendLine();
                }
    
                string partialQuery = objQuery.ToTraceString();
                objQuery.Parameters.ToList().ForEach(p => partialQuery = partialQuery.Replace(":" + p.Name, p.Value.ToString()));
    
                sb.AppendLine(partialQuery);
            }
    
            // replace 
    
            return sb.ToString();
        }
    

    【讨论】:

    • 在我实现上述方法后,我没有看到任何差异,那么上述扩展究竟会做什么?
    【解决方案3】:

    例如,将扩展方法名称更改为 TraceString2,因为 TraceString 与 ObjectQuery 方法名称相同。

    此扩展,显示查询参数的所有数据(@p_linq_0 ...):

    using (var context = new MyEntities(true))
    {
        string cpty = "MiCpty";
        DateTime date = DateTime.Today;
    
        var query = context.CECA_SPECIAL_CAS.Where(a => a.IDACUERDO == cpty 
                                                        && a.FECHAENTRADA == date);
        query.ToList();
    
        System.Diagnostics.Debug.Print(query.ToTraceString2());
    }
    

    结果:

    查询参数:

    名称 |类型 |价值

    p_linq_0 |System.String |MiCpty
    p_linq_1 |System.DateTime |31/10/2013 00:00:00

    选择 "Extent1"."IDACUERDO" 作为 "IDACUERDO", "Extent1"."NOMBREPRINCIPAL" AS "NOMBREPRINCIPAL", "Extent1"."NOMBRELEGAL" 作为 "NOMBRELEGAL", "Extent1"."NOMBRELARGO" 作为 "NOMBRELARGO", "Extent1"."FECHAENTRADA" 作为 "FECHAENTRADA", "Extent1"."DIASEMANAOPERAR" 作为 "DIASEMANAOPERAR", "Extent1"."TIPOCONTRATO" 作为 "TIPOCONTRATO", "Extent1"."PERIODICIDAD" AS "PERIODICIDAD", "Extent1"."DESC_PERIODICIDAD" AS "DESC_PERIODICIDAD", "Extent1"."ACTIVO" 作为 "ACTIVO", "Extent1"."MA_TYPE" AS "MA_TYPE", "Extent1"."MA_VERSION" AS "MA_VERSION", "Extent1"."COLLATERALPORTFOLIOCODE" AS "COLLATERALPORTFOLIOCODE" FROM "CKCOL_ADM"."CECA_SPECIAL_CA" "Extent1" WHERE (("Extent1"."IDACUERDO" = MiCpty) AND ("Extent1"."FECHAENTRADA" = 31/10/2013 00:00:00))

    【讨论】:

      【解决方案4】:

      您可以使用 sql server 2008 工具“Sql Server Profiler”查看执行的查询。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多