たくさんの質問をしているように感じますが、行き詰まっています。私は OData サービスを開発しています。ユーザー指定の名前と値のペアをいくつか関連付けることができるエンティティが必要であり、それらを検索することができます。EF4.3、DataServiceVersion 3.0 を使用しています。カスタムのメタデータ、クエリ、および更新プロバイダーを使用しています。
それでは、エンティティ Person があるとしましょう:
public class Person : EntityBase
{
public virtual IList<Property> PropertySet { get; set; }
}
(EntityBase は、すべてのエンティティが由来する共通の POCO です。Guid ID プロパティしかありません。) 次に、プロパティを定義しましょう。
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 を使用しています。
これで、Person にプロパティを追加できるようになりました。次のようなリクエストを行うと、次のようになります。
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」という名前のプロパティを持つ Person をクエリすると、次のように動作します。
GET /Service/People?$expand=PropertySet&$filter=PropertySet/any(x: x/Name eq 'EmailAddress')
しかし、そのためにも、いくつかのトリックを実行する必要がありました。式ビジターを実装し、Linq To Entities が気に入らないと思われるいくつかの比較を実行しました。
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 を持っています。) 2 番目のオーバーライドでは、"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 はエンティティ データ モデルのプリミティブ型のキャストのみをサポートしています。」だから...今、私は再び壁に頭をぶつけて立ち往生しています。継承が正しく設定されていない可能性がありますか? Convert を機能させるには、他の Expression Visitor メソッドをオーバーロードする必要がありますか? 継承されたプロパティを操作するように Linq To Entities を説得するにはどうすればよいですか?
ありがとう!