首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WebApi oData生成不兼容的SQL。

WebApi oData生成不兼容的SQL。
EN

Stack Overflow用户
提问于 2014-04-08 16:30:18
回答 2查看 633关注 0票数 0

我收到了错误:

在Server 2005之前的Server版本中,参数作为查询中的TOP子子句和限制子子句或命令树中的LimitExpression的参数不受支持。

当使用WebAPI + oData时,在LinqPad中执行Take()或通过http:

代码语言:javascript
复制
http://localhost:8080/odata/sample()?$top=10

但是,如果我直接针对DbContext运行了run (),它就能正常工作。因此,我假设oData魔术正在创建一些在我的设置中不支持的linq / sql。

复杂的是,需要设置EDMX才能在兼容模式80 (sql 2000)中运行。

而且我正在使用ASP.NET Web中的组合键 (但我现在明白这是另一个职位推荐的方法,所以很可能不是问题所在)。

它是从视野中而不是桌子上传来的。

我使用的是WebApi 1(而不是2)。

是否有一种方法可以覆盖正在生成的SQL,以避免不兼容的SQL?

编辑:

在浏览了webapi (然后是实体框架代码)之后,我发现了抛出异常的一行,这是一种有启发性的体验:

在\entityframework\src\EntityFramework.SqlServer\SqlGen\Sql8ConformanceChecker.cs中

代码语言:javascript
复制
/// <summary>
/// Walks the structure
/// </summary>
/// <exception cref="NotSupportedException">expression.Limit is DbParameterReferenceExpression</exception>
public override bool Visit(DbLimitExpression expression)
{
    Check.NotNull(expression, "expression");

    if (expression.Limit is DbParameterReferenceExpression)
    {
        throw new NotSupportedException(Strings.SqlGen_ParameterForLimitNotSupportedOnSql8);
    }

    return VisitExpression(expression.Argument);
}

但是,我仍然不知道为什么我可以轻松地针对上下文执行OrderBy().Take(),但是oData版本失败了。

我已经成功地比较了oData生成的oData与查询dbContext时生成的等效dbContext。

oData版本:

代码语言:javascript
复制
{DbQueryCommandTree
|_Parameters
| |_p__linq__0 : Edm.Int32
|_Query : Collection{Record['UserID'=Edm.Int32, 'RolledupReviewPeriodID'=Edm.Int32, 'UserEmployeeNumber'=Edm.String, 'FirstName'=Edm.String, 'LastName'=Edm.String, 'JobTitle'=Edm.String, 'CostCentre'=Edm.String, 'CostCentreNo'=Edm.String, 'Department'=Edm.String, 'Division'=Edm.String, 'Directorate'=Edm.String, 'AppraiserName'=Edm.String, 'ReviewDate'=Edm.DateTime, 'Status'=Edm.String, 'InterimRating'=Edm.Int32, 'IndicativeRating'=Edm.Int32, 'PreModerated'=Edm.Int32, 'ModeratedRating'=Edm.Int32, 'FinalRating'=Edm.Int32, 'ReviewPeriodName'=Edm.String]}
  |_Project
    |_Input : 'Limit1'
    | |_Limit
    |   |_Sort
    |   | |_Input : 'Extent1'
    |   | | |_Scan : PMM_ModelStoreContainer.vwReports_ReviewRatings
    |   | |_SortOrder
    |   |   |_Asc
    |   |   | |_Var(Extent1).RolledupReviewPeriodID
    |   |   |_Asc
    |   |     |_Var(Extent1).UserID
    |   |_@p__linq__0
    |_Projection
      |_NewInstance : Record['UserID'=Edm.Int32, 'RolledupReviewPeriodID'=Edm.Int32, 'UserEmployeeNumber'=Edm.String, 'FirstName'=Edm.String, 'LastName'=Edm.String, 'JobTitle'=Edm.String, 'CostCentre'=Edm.String, 'CostCentreNo'=Edm.String, 'Department'=Edm.String, 'Division'=Edm.String, 'Directorate'=Edm.String, 'AppraiserName'=Edm.String, 'ReviewDate'=Edm.DateTime, 'Status'=Edm.String, 'InterimRating'=Edm.Int32, 'IndicativeRating'=Edm.Int32, 'PreModerated'=Edm.Int32, 'ModeratedRating'=Edm.Int32, 'FinalRating'=Edm.Int32, 'ReviewPeriodName'=Edm.String]
        |_Column : 'UserID'
        | |_Var(Limit1).UserID
        |_Column : 'RolledupReviewPeriodID'
        | |_Var(Limit1).RolledupReviewPeriodID
        |_Column : 'UserEmployeeNumber'
        | |_Var(Limit1).UserEmployeeNumber
        |_Column : 'FirstName'
        | |_Var(Limit1).FirstName
        |_Column : 'LastName'
        | |_Var(Limit1).LastName
        |_Column : 'JobTitle'
        | |_Var(Limit1).JobTitle
        |_Column : 'CostCentre'
        | |_Var(Limit1).CostCentre
        |_Column : 'CostCentreNo'
        | |_Var(Limit1).CostCentreNo
        |_Column : 'Department'
        | |_Var(Limit1).Department
        |_Column : 'Division'
        | |_Var(Limit1).Division
        |_Column : 'Directorate'
        | |_Var(Limit1).Directorate
        |_Column : 'AppraiserName'
        | |_Var(Limit1).AppraiserName
        |_Column : 'ReviewDate'
        | |_Var(Limit1).ReviewDate
        |_Column : 'Status'
        | |_Var(Limit1).Status
        |_Column : 'InterimRating'
        | |_Var(Limit1).InterimRating
        |_Column : 'IndicativeRating'
        | |_Var(Limit1).IndicativeRating
        |_Column : 'PreModerated'
        | |_Var(Limit1).PreModerated
        |_Column : 'ModeratedRating'
        | |_Var(Limit1).ModeratedRating
        |_Column : 'FinalRating'
        | |_Var(Limit1).FinalRating
        |_Column : 'ReviewPeriodName'
          |_Var(Limit1).ReviewPeriodName}

直接按下 dbContext 时的版本

代码语言:javascript
复制
{DbQueryCommandTree
|_Parameters
|_Query : Collection{Record['UserID'=Edm.Int32, 'RolledupReviewPeriodID'=Edm.Int32, 'UserEmployeeNumber'=Edm.String, 'FirstName'=Edm.String, 'LastName'=Edm.String, 'JobTitle'=Edm.String, 'CostCentre'=Edm.String, 'CostCentreNo'=Edm.String, 'Department'=Edm.String, 'Division'=Edm.String, 'Directorate'=Edm.String, 'AppraiserName'=Edm.String, 'ReviewDate'=Edm.DateTime, 'Status'=Edm.String, 'InterimRating'=Edm.Int32, 'IndicativeRating'=Edm.Int32, 'PreModerated'=Edm.Int32, 'ModeratedRating'=Edm.Int32, 'FinalRating'=Edm.Int32, 'ReviewPeriodName'=Edm.String]}
  |_Project
    |_Input : 'Limit1'
    | |_Limit
    |   |_Sort
    |   | |_Input : 'Extent1'
    |   | | |_Scan : PMM_ModelStoreContainer.vwReports_ReviewRatings
    |   | |_SortOrder
    |   |   |_Asc
    |   |   | |_Var(Extent1).RolledupReviewPeriodID
    |   |   |_Asc
    |   |     |_Var(Extent1).UserID
    |   |_10
    |_Projection
      |_NewInstance : Record['UserID'=Edm.Int32, 'RolledupReviewPeriodID'=Edm.Int32, 'UserEmployeeNumber'=Edm.String, 'FirstName'=Edm.String, 'LastName'=Edm.String, 'JobTitle'=Edm.String, 'CostCentre'=Edm.String, 'CostCentreNo'=Edm.String, 'Department'=Edm.String, 'Division'=Edm.String, 'Directorate'=Edm.String, 'AppraiserName'=Edm.String, 'ReviewDate'=Edm.DateTime, 'Status'=Edm.String, 'InterimRating'=Edm.Int32, 'IndicativeRating'=Edm.Int32, 'PreModerated'=Edm.Int32, 'ModeratedRating'=Edm.Int32, 'FinalRating'=Edm.Int32, 'ReviewPeriodName'=Edm.String]
        |_Column : 'UserID'
        | |_Var(Limit1).UserID
        |_Column : 'RolledupReviewPeriodID'
        | |_Var(Limit1).RolledupReviewPeriodID
        |_Column : 'UserEmployeeNumber'
        | |_Var(Limit1).UserEmployeeNumber
        |_Column : 'FirstName'
        | |_Var(Limit1).FirstName
        |_Column : 'LastName'
        | |_Var(Limit1).LastName
        |_Column : 'JobTitle'
        | |_Var(Limit1).JobTitle
        |_Column : 'CostCentre'
        | |_Var(Limit1).CostCentre
        |_Column : 'CostCentreNo'
        | |_Var(Limit1).CostCentreNo
        |_Column : 'Department'
        | |_Var(Limit1).Department
        |_Column : 'Division'
        | |_Var(Limit1).Division
        |_Column : 'Directorate'
        | |_Var(Limit1).Directorate
        |_Column : 'AppraiserName'
        | |_Var(Limit1).AppraiserName
        |_Column : 'ReviewDate'
        | |_Var(Limit1).ReviewDate
        |_Column : 'Status'
        | |_Var(Limit1).Status
        |_Column : 'InterimRating'
        | |_Var(Limit1).InterimRating
        |_Column : 'IndicativeRating'
        | |_Var(Limit1).IndicativeRating
        |_Column : 'PreModerated'
        | |_Var(Limit1).PreModerated
        |_Column : 'ModeratedRating'
        | |_Var(Limit1).ModeratedRating
        |_Column : 'FinalRating'
        | |_Var(Limit1).FinalRating
        |_Column : 'ReviewPeriodName'
          |_Var(Limit1).ReviewPeriodName}

大不相同的是它传递给参数的oData版本:

代码语言:javascript
复制
|_Parameters | |_p__linq__0 : Edm.Int32

而dbcontext版本则直接传递参数,即"10“。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-04-09 14:31:14

在洞里挖得像鼹鼠一样深(有点疯了)。

我偶然发现这块宝石:

代码语言:javascript
复制
 public static IQueryable Take(IQueryable query, int count, Type type, bool parameterize)
 {
     MethodInfo takeMethod = ExpressionHelperMethods.QueryableTakeGeneric.MakeGenericMethod(type);
     Expression takeValueExpression = parameterize ? LinqParameterContainer.Parameterize(typeof(int), count) : Expression.Constant(count);

     Expression takeQuery = Expression.Call(null, takeMethod, new[] { query.Expression, takeValueExpression });
     return query.Provider.CreateQuery(takeQuery);
 }

内部ExpressionHelpers.cs

我想知道改变参数的效果是什么:

Expression takeValueExpression = parameterize ? LinqParameterContainer.Parameterize(typeof(int), count) : Expression.Constant(count);

爬回堆栈,我可以看到它是通过混合请求和上下文来创建ODataQuerySettings.EnableConstantParameterization传入的。

我可以在控制器中重写哪个读取直接调用查询选项 (太棒了!)

因此,在控制器中修复的方法很简单:

代码语言:javascript
复制
 public IQueryable<T> Get(ODataQueryOptions<T> options)
 {
     ODataQuerySettings settings = new ODataQuerySettings()
     {
         EnableConstantParameterization = false
     };

     IQueryable results = options.ApplyTo(db.T.AsQueryable(), settings);

     return results as IQueryable<T>;
 }
票数 1
EN

Stack Overflow用户

发布于 2014-04-09 18:09:59

亚历克斯,很高兴你已经找到了解决这个问题的方法。我们添加了这个配置设置EnableConstantParameterization`,以提高EF和SQL查询缓存性能。在默认情况下,我们打开它,因为我们已经看到了非常好的性能改进。不幸的是,正如您已经注意到的,它可能不适用于较早版本的SQL server。

如果使用的是ODataQuerySettings.EnableConstantParameterization,则可以使用ODataQueryOptions禁用此设置。如果您使用的是QueryableAttribute,可以通过设置[QueryableAttribute(EnableConstantParameterization = false)]来禁用它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22942935

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档