0

我覺得我問了很多問題,但我一直陷入困境。我正在開發一個OData服務,並且我想要一個可以有多個用戶指定的名稱 - 值對關聯的實體,然後可以對其進行搜索。我正在使用EF4.3,DataServiceVersion 3.0。我正在使用自定義的元數據,查詢和更新提供程序。用繼承實體過濾OData請求導致鑄造異常

所以我們可以說,我有一個實體的人:(EntityBase是一種常見的POCO,我所有的實體來自;它只有一個GUID ID屬性)

public class Person : EntityBase 
{ 
    public virtual IList<Property> PropertySet { get; set; } 
} 

現在讓我們來定義屬性:

public abstract class Property : EntityBase 
{ 
    public string Name { get; set; } 
    public Person Person { get; set; } 
    public Guid PersonId { get; set; } 
} 

public class IntProperty : Property 
{ 
    public int? IValue { get; set; } 
} 

public class StringProperty : Property 
{ 
    public string SValue { get; set; } 
} 

到目前爲止,這麼好。在我的配置中,我使用Table Per Hierarchy來進行繼承。

現在,我可以將屬性添加到我的人,當我提出這樣的要求:

GET /Service/People(guid'THE_ID')?$expand=PropertySet 

它的工作原理:

{"d": { 
    "__metadata": {...}, 
    "PropertySet": { 
    "results": [{ 
     "__metadata": {...}, 
     "Id": "PROP_1_ID", 
     "Name": "Number", 
     "IValue": 1234 
    },{ 
     "__metadata": {...}, 
     "Id": "PROP_2_ID", 
     "Name": "EmailAddress", 
     "SValue": "AAAA" 
    }] 
    }, 
    "Id": "THE_ID", 
    } 
} 

如果我查詢一個人說有一個名爲'EmailAddress'的屬性,它的工作原理如下:

GET /Service/People?$expand=PropertySet&$filter=PropertySet/any(x: x/Name eq 'EmailAddress') 

但即便如此,我還得拉幾個竅門。我實現了一個表達式遊客和重擊了幾個比較是LINQ到實體似乎並不喜歡:

protected override Expression VisitBinary(BinaryExpression node) 
{ 
    if (node.NodeType == ExpressionType.Equal) 
    { 
    Expression left = Visit(node.Left); 
    Expression right = Visit(node.Right); 

    ConstantExpression rightConstant = right as ConstantExpression; 
    if (null != rightConstant && rightConstant.Value == null) 
    { 
     if (left.Type == typeof(IList<Property>)) 
     { 
     return Expression.Constant(false, typeof(bool)); 
     } 
    } 
    } 
    return base.VisitBinary(node); 
} 

protected override Expression VisitConditional(ConditionalExpression node) 
{ 
    Expression visitedTest = Visit(node.Test); 
    Expression visitedIfTrue = Visit(node.IfTrue); 
    Expression visitedIfFalse = Visit(node.IfFalse); 
    ConstantExpression constantTest = visitedTest as ConstantExpression; 
    if (null != constantTest && constantTest.Value is bool) 
    { 
    return ((bool)constantTest.Value) ? visitedIfTrue : visitedIfFalse; 
    } 
    return Expression.Condition(visitedTest, visitedIfTrue, visitedIfFalse); 
} 

要點是與第一重寫我的查詢獲取諸如「it.PropertySet == NULL 「,我知道這將永遠是不真實的。 (在我的秒殺中,唯一有PropertySet的是Person,而Person總是有一個PropertySet。)在第二個覆蓋中,我正在查看諸如「IIF((it.PropertySet == null),Empty (),it.PropertySet)「,我知道」it「將始終有一個PropertySet。這可以防止將IList與null進行比較的錯誤。

現在,問題所在。

僅僅尋找物業的存在是不夠的。我想檢查它的值:

GET /Service/People?$expand=PropertySet&$filter=PropertySet/any(x: x/Name eq 'EmailAddress' and cast(x, 'InheritedPropertyTest.Entities.StringProperty')/SValue eq 'AAAA') 

這是結果查詢:

value(System.Data.Objects.ObjectQuery`1[InheritedPropertyTest.Entities.Person]) 
.MergeAs(AppendOnly) 
.Where(it => it.PropertySet.Any(x => ((x.Name == "EmailAddress") AndAlso (IIF((Convert(x) == null), null, Convert(x).SValue) == "AAAA")))) 
.OrderBy(p => p.Id) 
.Take(100) 
.Select(p => new ExpandedWrapper`2() {ExpandedElement = p, Description = "PropertySet", ReferenceDescription = "", ProjectedProperty0 = p.PropertySet.OrderBy(p => p.Id) .Take(100)}) 

但我得到這個錯誤:「無法轉換類型‘InheritedPropertyTest.Entities.Property’到類型'InheritedPropertyTest.Entities.StringProperty'。LINQ to Entities只支持投射實體數據模型原語類型。「所以...現在我卡住了我的頭靠牆再次。也許我的繼承設置不正確?我是否需要重載一些其他Expression Visitor方法來進行Convert工作?我如何說服Linq To Entities使用繼承的屬性?

謝謝!

回答

1

而不是轉換,使用類型段。因此,例如x/InheritedPropertyTest.Entities.StringProperty/SValue 應該將其轉換爲EF應該能夠處理的TypeOf。

請注意,雖然自定義提供程序通過EF將有很多問題無論如何。簡化表達式的一種方法是轉向空傳播(IDataServiceQueryProvider.IsNullPropagationRequired = false)。這應該擺脫IFF(i == null,null,something)。 但是你仍然會遇到問題,特別是當你開始使用投影時($ select)。

+0

嗯,我試過了,它的工作完美無瑕。我從來沒有見過這種語法,謝謝! 至於自定義提供程序的東西...是的,這是棘手的,但我們迄今做得很好。正是這種繼承和開放式的東西讓我頭痛不已。我認爲我們由於某種原因打開了空傳播,但現在我不記得了,應該重新審視它。我想,取消空值支票是一個小的代價。 再次感謝您的協助! – object88