无法将过滤器应用于odata中的集合对象的属性

时间:2017-11-12 13:32:43

标签: c# entity-framework odata

我在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;
        }
    }
}

DbContext种子数据和DbInitializer

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实体,其状态可用,如new,active,inprogress等。当我尝试对这些值进行本地化时,我发现拥有文本类/表的方法很容易。因此创建了ProjectStatusTexts类/表。

当我尝试使用已翻译的值获取项目时,我使用浏览器中的以下OData查询,但它不会过滤记录,因为EF为任何子句返回ProjectStatusTexts表中的所有记录。

我也尝试过StackOverflow中给出的以下方法,但无法修复。这个模型有什么问题,C#不理解或者OData查询URI有问题,请帮助。

查询URI:http://localhost:64046/odata/Projects $扩大=状态/ ProjectStatusTexts&安培; $滤波器=状态/ ProjectStatusTexts /任何(1:1 /语言/ LanguageId%20当量%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);

2 个答案:

答案 0 :(得分:0)

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

http://localhost:64046/odata/Projects?$扩大=状态/ ProjectStatusTexts($滤波器=语言/ LanguageId%20当量%202)

答案 1 :(得分:0)

  

@TomDoesCode

的答案为基础

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

要实现这一点,我们需要扩展两次,在扩展到ProjectStatusTexts中,我们还需要应用过滤器。

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

此查询将返回所有项目,但如果其语言ID与“英语”(2)相匹配,则仅包含ProjectStatusText

  

请注意,要访问多个级别的导航属性,我们必须使用嵌套的$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)

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