0

我收到的錯誤:的WebAPI的OData產生不兼容SQL

Parameters as arguments to a TOP sub-clause and a LIMIT sub-clause in a query, or a LimitExpression in a command tree, are not supported in versions of SQL Server earlier than SQL Server 2005.

當LinqPad或通過HTTP做Take()時使用的WebAPI +的OData:

http://localhost:8080/odata/sample()?$top=10 

但是,如果我運行一個拿( )直接對DbContext它工作正常。所以我認爲oData魔術創建了一些linq/sql,這在我的設置中不受支持。

我遇到的困難是EDMX需要設置爲在兼容模式下運行80(sql 2000)。

而且我使用的是Composite Key in ASP.NET Web API OData(但我現在明白這是the recommended approach from another post所以可能不是問題)。

它來自一個視圖,而不是一張桌子。

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

是否有一種方法來覆蓋正在生成的SQL,以便我可以避免不兼容的SQL?

編輯:

通過的WebAPI步進(然後實體框架代碼)後 - 一個啓發性的經驗,我發現,拋出異常的行:

在\的EntityFramework的\ src \ EntityFramework.SqlServer \ SqlGen \ Sql8ConformanceChecker.cs

/// <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); 
} 

但我還是不知道爲什麼,我可以很容易地做一個排序依據()。以()反對的背景下,但OData的版本失敗。

我設法比較了由oData生成的DbQueryCommandTree與查詢dbContext時生成的等價物。

的OData的版本:打黑的DbContext時

{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 
    | |[email protected]__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} 

版本直接

{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的版本,它通過在PARAMATERS:

|_Parameters | |_p__linq__0 : Edm.Int32 

鑑於的DbContext版本的參數只是在directtly即「10」

回答

1

通過深挖掘如在孔的摩爾後(走略微MAD)。

我碰到這種寶石:

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

我想知道是什麼改變paramterize的影響的是:

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

爬回了堆棧我可以看到它是通過在混合請求和上下文創建ODataQuerySettings.EnableConstantParameterization

哪讀書Invoking Query Options Directly我可以在控制器覆蓋(優秀!)

所以解決方法是簡單地在控制器中的以下內容:

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

亞歷克斯,很好,你已經找到了解決這個問題。我們添加了這個配置設置,EnableConstantParameterization`,用於提高EF和SQL查詢緩存的性能。我們在默認情況下將其打開,因爲我們已經看到在啓用此功能後確實取得了很好的性能提升不幸的是,正如你已經注意到的那樣,它可能不適用於老版本的SQL服務器。

如果您使用的是ODataQueryOptions,則可以使用ODataQuerySettings.EnableConstantParameterization來禁用此設置。如果您使用的是QueryableAttribute,則可以通過設置[QueryableAttribute(EnableConstantParameterization = false)]來禁用此功能。

+0

感謝RaghuRam,當我遇到「EnableConstantParameterization」我認爲可能有一個ActionFilterAttribute隱藏在某處我找不到它:-)我猜這在WebAPI2中被稱爲[Queryable],但在WebAPI 1中被稱爲[QueryableAttribute]? –

+0

btw。這是一個很棒的功能,我可能是想用SQL 2000新技術(webapi)的邊緣案例:-) –

+0

我現在可以看到,引入命名空間[Queryable]工作(和[QueryableAttribute])非常好。 –