【问题标题】:Not able to apply filter to the property of a collection object in odata无法将过滤器应用于 odata 中集合对象的属性
【发布时间】:2017-11-12 13:32:43
【问题描述】:

我在 C# Web API OData 中使用以下代码并尝试对 LanguageId 进行过滤,但在应用过滤器时出现以下错误

模型

namespace ODataSample
{
    public class Project
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public DateTime CreatedOn { get; set; }
        public DateTime UpdatedOn { get; set; }
        public long CreatedBy { get; set; }
        public long UpdatedBy { get; set; }
        public decimal Cost { get; set; }
        public long StatusId { get; set; }
        [ForeignKey("StatusId")]
        public virtual ProjectStatus Status { get; set; }
    }

    public class ProjectStatus
    {
        public ProjectStatus()
        {
            ProjectStatusTexts = new HashSet<ProjectStatusText>();
        }
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long StatusId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public bool IsActive { get; set; }
        public virtual ICollection<ProjectStatusText> ProjectStatusTexts { get; set; }
        public virtual ICollection<Project> Projects { get; set; }
    }

    public class ProjectStatusText
    {
        public ProjectStatusText()
        {
            ProjectStatus = new HashSet<ProjectStatus>();
        }

        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }
        public long StatusId { get; set; }
        [ForeignKey("StatusId")]
        public IEnumerable<ProjectStatus> ProjectStatus { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public long LanguageId { get; set; }
        [ForeignKey("LanguageId")]
        public virtual Language Language { get; set; }
        public bool IsActive { get; set; }
    }

    public class Language
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long LanguageId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public bool IsActive { get; set; }
    }

    public class User
    {
        public User()
        {
            this.Projects = new HashSet<Project>();
        }

        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long UserId { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public bool IsActive { get; set; }
        public virtual ICollection<Project> Projects { get; set; }
    }
}

上述模型的服务

namespace ODataSample.Services
{
    public class ProjectsService
    {
        public IQueryable<Project> GetProjects()
        {
            var ctx = new ProjectsContext();

            return ctx.Projects;
        }
    }
}

带有种子数据的DbContextDbInitializer

namespace ODataSample.Contexts
{
    public class ProjectsContext : DbContext
    {
        public ProjectsContext() : base("ProjectsConnectionString") { Database.SetInitializer(new ProjectDBInitializer()); }
        public ProjectsContext(string nameOrConnectionString) : base(nameOrConnectionString)
        {
            Database.SetInitializer(new ProjectDBInitializer());
        }

        public DbSet<Project> Projects { get; set; }
        public DbSet<User> Users { get; set; }
        public DbSet<ProjectStatus> ProjectStatuses { get; set; }
        public DbSet<ProjectStatusText> ProjectStatusTexts { get; set; }
        public DbSet<Language> Languages { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }

    public class ProjectDBInitializer : DropCreateDatabaseAlways<ProjectsContext>
    {
        public ProjectDBInitializer()
        {
        }

        protected override void Seed(ProjectsContext context)
        {
            context.Languages.AddRange(new[]
            {
                new Language { Description = "Spanish", IsActive = true, LanguageId = 1, Name = "es" },
                new Language { Description = "English", IsActive = true, LanguageId = 2, Name = "en" }
            });

            context.ProjectStatuses.AddRange(new[]
            {
                new ProjectStatus { StatusId=1, Name="Active", Description="Active", IsActive= true },
                new ProjectStatus { StatusId=2, Name="Closed", Description="Closed", IsActive= true },
                new ProjectStatus { StatusId=3, Name="InProgress", Description="In Progress", IsActive= true }
            });

            context.ProjectStatusTexts.AddRange(new[]
            {
                new ProjectStatusText {Id = 1, LanguageId=1, StatusId = 1,Name = Path.GetRandomFileName(),Description = Path.GetRandomFileName(),IsActive=true },
                new ProjectStatusText {Id = 2, LanguageId=2, StatusId = 1,Name = "Active",Description = "Active",IsActive=true },
                new ProjectStatusText {Id = 3, LanguageId=1, StatusId = 2,Name = Path.GetRandomFileName(),Description = Path.GetRandomFileName(),IsActive=true },
                new ProjectStatusText {Id = 4, LanguageId=2, StatusId = 3,Name = "InProgress",Description = "InProgress",IsActive=true }
            });

            context.Projects.AddRange(new[]
            {
                new Project{ Cost = default(decimal),  Id=1,Name="project 1",StatusId = 1, Description="project 1", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now,  CreatedBy = 1, UpdatedBy =1},
                new Project{ Cost = default(decimal), Id=2,Name="project 2",StatusId = 1, Description="project 2", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now,  CreatedBy = 1, UpdatedBy =1},
                new Project{ Cost = default(decimal), Id=3,Name="project 3",StatusId = 1, Description="project 3", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now, CreatedBy = 1, UpdatedBy =1 }
            });

            base.Seed(context);
        }
    }
}

以下是WebApiConfig的注册情况

var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Project>("Projects");
modelBuilder.EntitySet<ProjectStatus>("ProjectStatusses");
modelBuilder.EntitySet<ProjectStatusText>("ProjectStatusTexts");
modelBuilder.EntitySet<Language>("Languages");
modelBuilder.EntitySet<User>("Users");
var edmModel = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("odata", "odata", edmModel);

问题: 我有Project 实体,其状态可用,如新、活动、进行中等。当我尝试对这些值进行本地化时,我发现拥有文本类/表的方法会很容易。因此创建了ProjectStatusTexts 类/表。

当我尝试使用翻译后的值获取项目时,我正在使用浏览器中的以下 OData 查询,但它不会过滤记录,因为 EF 为 any生成的 where 条件> URI 中的子句返回ProjectStatusTexts 表中的所有记录。

我也尝试过 StackOverflow 中给出的以下方法,但无法修复。这个模型有什么问题 C# 看不懂还是 OData Query URI 有问题,请帮忙。

查询 URI: http://localhost:64046/odata/Projects?$expand=Status/ProjectStatusTexts&$filter=Status/ProjectStatusTexts/any(l:l/Language/LanguageId%20eq%202)

var projects = (EdmEntitySet)edmModel.EntityContainers().Single().FindEntitySet("Projects");
var projectStatusTexts = (EdmEntitySet)edmModel.EntityContainers().Single().FindEntitySet("ProjectStatusTexts");
var projectType = (EdmEntityType)edmModel.FindDeclaredType("ODataSample.Project");
var projectStatusTextsType = (EdmEntityType)edmModel.FindDeclaredType("ODataSample.ProjectStatusText");

var partsProperty = new EdmNavigationPropertyInfo();
partsProperty.TargetMultiplicity = EdmMultiplicity.Many;
partsProperty.Target = projectStatusTextsType;
partsProperty.ContainsTarget = false;
partsProperty.OnDelete = EdmOnDeleteAction.Cascade;
partsProperty.Name = "ProjectStatusTexts";

//projects.AddNavigationTarget(projectType.AddUnidirectionalNavigation(partsProperty), projectStatusTexts);

var navigationProperty = projectType.AddUnidirectionalNavigation(partsProperty);
projects.AddNavigationTarget(navigationProperty, projectStatusTexts);

var linkBuilder = edmModel.GetEntitySetLinkBuilder(projects);
linkBuilder.AddNavigationPropertyLinkBuilder(navigationProperty,
    new NavigationLinkBuilder((context, property) =>
        context.GenerateNavigationPropertyLink(property, false), true));

config.Routes.MapODataRoute("odata", "odata", edmModel);

【问题讨论】:

  • 我对你的数据模型有点不清楚,是否可以提供更多关于这些东西代表什么以及你试图过滤什么的细节?
  • 我正在尝试按给定语言过滤项目状态。该项目有一个从 ProjectStatus 获取的 statusid,它是本地化的,并且本地化数据可从 ProjectStatusTexts 表中获得每种语言。当我得到一个项目时,我想按用户的语言显示 StatusText。例如:西班牙语

标签: c# entity-framework odata


【解决方案1】:

我认为您想要实现的是获得所有项目,然后扩展到具有正确语言的ProjectStatusTexts。如果是这种情况,则过滤器是扩展的一部分,而不是 Projects 本身的查询的一部分,因此应在扩展后将其添加到括号中,如下所示:

http://localhost:64046/odata/Projects?$expand=Status/ProjectStatusTexts($filter=Language/LanguageId%20eq%202)

【讨论】:

  • 我收到以下错误Term 'Status/ProjectStatusTexts($filter=Language/LanguageId eq 2)' is not valid in a $select or $expand expression
  • 这个 URL http://localhost:64046/odata/Projects?$expand=Status/ProjectStatusTexts($filter=LanguageId%20eq%202) 也给出了Term 'Status/ProjectStatusTexts($filter=LanguageId eq 2)' is not valid in a $select or $expand expression 错误
【解决方案2】:

基于@TomDoesCode 的回答

在 OP 的架构中,Project 有一个 Status,而Status 有很多 ProjectStatusText 在结果集中,我们只想包含与给定语言 id 匹配的 ProjectStatusText 记录。

为此,我们需要扩展两次,在扩展至ProjectStatusTexts 时,我们还需要应用过滤器。

http://localhost:64046/odata/Projects?$expand=Status($expand=ProjectStatusTexts($filter=Language/LanguageId eq 2))

此查询将返回所有项目,但仅包括 ProjectStatusText 的语言 ID 匹配“英语”(2)

注意,要访问多个级别的导航属性,我们必须使用嵌套的$expand 运算符


尝试过滤的 OP 可以大致翻译为:

  • 具有英文文本的任何状态的项目过滤

如果您还想只返回英文状态的项目,那么您可以将这两种方法组合成以下方式:

http://localhost:64046/odata/Projects?$expand=Status($expand=ProjectStatusTexts($filter=Language/LanguageId eq 2))&$filter=Status/ProjectStatusTexts/any(l:l/Language/LanguageId eq 2)

但是,这种类型的过滤器通常是多余的,特别是如果所有项目状态记录都针对每种支持的语言都有一个条目。

【讨论】:

    猜你喜欢
    • 2012-10-11
    • 2016-12-03
    • 2022-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 2019-05-05
    相关资源
    最近更新 更多