我觉得我问了很多问题,但我一直卡住。我正在开发 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”的属性的人,则可以:
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。)在第二个覆盖中,我正在查看诸如 "IIF((it.PropertySet == null), Empty (), it.PropertySet)”,我知道“它”总是有一个 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 仅支持转换实体数据模型原始类型。” 所以......现在我又被困在墙上撞我的头了。也许我的继承设置不正确?我是否需要重载其他一些表达式访问器方法才能使转换工作?如何说服 Linq To Entities 使用继承的属性?
谢谢!