C# 在计算属性上使用contains时,OData v4筛选器查询失败

C# 在计算属性上使用contains时,OData v4筛选器查询失败,c#,asp.net-web-api,entity-framework-6,odata,C#,Asp.net Web Api,Entity Framework 6,Odata,所以我有一个代码优先的EF 6层,它有一个接触类: public class Contact { [Key] public int Id { get; set; } [MaxLength(50)] public string Prefix { get; set; } [MaxLength(50)] public string Suffix { get; set; } [MaxLength(50)] public string

所以我有一个代码优先的EF 6层,它有一个接触类:

public class Contact
{
    [Key]
    public int Id { get; set; }

    [MaxLength(50)]
    public string Prefix { get; set; }

    [MaxLength(50)]
    public string Suffix { get; set; }

    [MaxLength(50)]
    public string FirstName { get; set; }

    [MaxLength(50)]
    public string MiddleName { get; set; }

    [MaxLength(50)]
    public string LastName { get; set; }

    [NotMapped]
    [DisplayName("Full Name")]
    public string FullName
    {
        get
        {
            string tempName =
                (!string.IsNullOrEmpty(Prefix) ? Prefix + " " : "") +
                (!string.IsNullOrEmpty(FirstName) ? FirstName + " " : "") +
                (!string.IsNullOrEmpty(MiddleName) ? MiddleName + " " : "") +
                (!string.IsNullOrEmpty(LastName) ? LastName + " " : "") +
                (!string.IsNullOrEmpty(Suffix) ? Suffix + " " : "");
            return tempName.Trim();
        }
    }

    [MaxLength(50)]
    public string JobTitle { get; set; }

    public bool? Primary { get; set; }
    public bool? Inactive { get; set; }

    public int? Customer_Id { get; set; }

    [ForeignKey("Customer_Id")]
    public virtual Customer Customer { get; set; }

    public virtual ICollection<Email> Emails { get; set; }
    public virtual ICollection<Address> Addresses { get; set; }
    public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
    public virtual ICollection<Note> Notes { get; set; }
}
公共类联系人
{
[关键]
公共int Id{get;set;}
[MaxLength(50)]
公共字符串前缀{get;set;}
[MaxLength(50)]
公共字符串后缀{get;set;}
[MaxLength(50)]
公共字符串名{get;set;}
[MaxLength(50)]
公共字符串MiddleName{get;set;}
[MaxLength(50)]
公共字符串LastName{get;set;}
[未映射]
[显示名称(“全名”)]
公共字符串全名
{
得到
{
字符串临时名称=
(!string.IsNullOrEmpty(前缀)?前缀+“”:“”)+
(!string.IsNullOrEmpty(FirstName)?FirstName+:“”)+
(!string.IsNullOrEmpty(MiddleName)?MiddleName+:“”)+
(!string.IsNullOrEmpty(LastName)?LastName+:“”)+
(!string.IsNullOrEmpty(后缀)?后缀+“”:“”);
返回tempName.Trim();
}
}
[MaxLength(50)]
公共字符串JobTitle{get;set;}
公共布尔?主{get;set;}
公共bool?非活动{get;set;}
public int?Customer_Id{get;set;}
[外键(“客户Id”)]
公共虚拟客户客户{get;set;}
公共虚拟ICollection电子邮件{get;set;}
公共虚拟ICollection地址{get;set;}
公共虚拟ICollection电话号码{get;set;}
公共虚拟ICollection注释{get;set;}
}
我有一个ASP.NET Web API 2服务正在运行,它提供了一个联系人列表,但是当我执行OData查询$filter=contains(tolower(全名)、tolower('smith'))时,我得到了一个错误的请求响应。我在WebAPI get方法中验证了它是否成功地从数据库中获取结果,但它返回了一个BadRequest错误

它肯定与FullName字段有关,要么是计算字段,要么是因为它具有NotMapped属性。当我将OData查询更改为$filter=contains(tolower(LastName)、tolower('smith'))时,它工作正常。我还尝试在查询中使用显示名“全名”而不是“全名”,但这也不起作用

我需要做些什么才能使OData在计算字段或未映射字段中发挥作用?

在您的Contacts控制器上实现一个控件,该控件接受一个字符串进行比较,并返回过滤后的联系人集。比如:

    [HttpGet]
    [ODataRoute("Contacts/Default.FullNameContains(value={value})")]
    public IHttpActionResult FullNameContains(string value)
    {
        value = value.ToLower();
        return Ok(db.Contacts.ToList().Where(c => c.FullName.Contains(value)));
    }

由于计算了
FullName
,因此该函数必须在内存中执行过滤。

@lencharest的回答将给出正确的结果,但请记住,为了执行过滤,您将把所有数据拉入内存。如果你有100个联系人,那没什么大不了的,但是如果你有很多联系人呢

我将在数据库中创建一个包含全名逻辑的视图。然后,将此视图作为实体公开。然后,可以在数据库中进行筛选,您可以拥有一个完全可查询的实体

更新:
稍微考虑一下,更好的方法是在表中为FullName指定一个计算列。假设您最终可能需要支持其他动词(POST、PATCH等),那么在表中有完整的实体定义将使事情保持简单明了。通过实现OData函数,您实际上已经放弃了可查询的OData模型。它们有自己的位置,但通常用于复杂的多实体操作,而不是对基本实体的单个属性实施筛选。

您的操作方法是否用
启用查询
属性修饰?@lencharest no,但是我的WebApiConfig中有以下命令,可以在我的所有控制器上启用OData查询:System.Web.Http.OData.Extensions.HttpConfigurationExtensions.AddODataQueryFilter(config);啊,你用的是OData3.0。您不使用版本4.0的任何原因?@lencharest实际上是Microsoft.AspNet.WebApi.OData 5.7.0。@lencharest我没有程序集,因此没有System.Web.OData的参考。我在框架和扩展中搜索了一个参考,以及Nuget。没有什么。它是否使用不同的Nuget包名称?我已经安装了Microsoft.AspNet.WebApi.OData包和依赖项。请记住,如果您的联系人数据库有大量行,您将把它们全部拉到内存中以执行筛选。OData函数感觉很笨拙,但我想OData确实没有其他方法。这感觉像一只耀眼的虫子。我确信在人们希望对其执行OData查询的类上有很多计算字段。另一种方法是:在
Get
方法中添加
ODataQueryOptions
参数;扫描
opts.Filter.FilterClause.Expression
中的计算属性;如果过滤器“脏”,保存原始过滤器,重写请求URI并重新生成查询选项,如中所述;执行查询;最后,将原始过滤器应用于查询结果(同样,在内存中)。此方法将允许您继续使用
$filter
语法。现在什么感觉笨重?;-)@lencharest,我理解你的答案,问题是“必须”这个词。必须记住它才能按照你的方式去做。但是,如果你有一百万个联系人,你的解决方案很糟糕,无法扩展。我想将计算字段移动到DB是可以的,但感觉更像是一种变通方法,而不是解决方案。特别是当您开始讨论编码人员是否应该关注DB实现时。也许,但您所做的是让数据库公开实体的完整实现。另外,我甚至可以在表中将FullName作为计算列(与视图相反)。这将简化其他操作的实施,等等。。。