【问题标题】:EF core: filter records matching any of the items in a listEF核心:过滤匹配列表中任何项目的记录
【发布时间】:2016-11-02 23:43:28
【问题描述】:

(已编辑)我正在使用文档表和文档三元组表来试验 EF 核心。每个文档可以包含 0 个或多个 (RDF) 三元组,由谓词加对象表示。

对应的EF实体是:

public sealed class Document
{
    public string Id { get; set; }
    public string CreatorId { get; set; }
    public string CategoryId { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    // ...
    public IList<DocumentTriple> DocumentTriples { get; set; }
}

和:

public sealed class DocumentTriple
{
    public int Id { get; set; }
    public string DocumentId { get; set; }
    public string Predicate { get; set; }
    public string Object { get; set; }
    public Document Document { get; set; }
}

他们的 SQL 定义:

CREATE TABLE [dbo].[Document](
    [Id] [varchar](32) NOT NULL,
    [CreatorId] [varchar](50) NOT NULL,
    [CategoryId] [varchar](20) NOT NULL,
    [Title] [nvarchar](500) NOT NULL,
    [Author] [nvarchar](500) NOT NULL,
    /* ... */
 CONSTRAINT [PK_dbo.Document] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[DocumentTriple](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [DocumentId] [varchar](32) NOT NULL,
    [Predicate] [varchar](100) NOT NULL,
    [Object] [nvarchar](1000) NOT NULL,
 CONSTRAINT [PK_DocumentTriple] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[DocumentTriple]  WITH CHECK ADD  CONSTRAINT [FK_dbo.DocumentTriple_dbo.Document_DocumentId] FOREIGN KEY([DocumentId])
REFERENCES [dbo].[Document] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[DocumentTriple] CHECK CONSTRAINT [FK_dbo.DocumentTriple_dbo.Document_DocumentId]
GO

现在,我想对文档应用一些可变过滤器,使用这种模式:我首先创建一个 IQueryable documents 对象,包括三元组,然后添加越来越多的过滤器,例如 @987654326 @。

其中一个过滤器是Tuple 的列表,其中 1 = 谓词过滤器值和 2=null 或对象过滤器值。我只想获取那些三元组中的任何一个至少匹配一个过滤谓词或谓词/值对的文档。

在 SQL 中,我可以这样做:

select * from document
inner join documenttriple on documenttriple.documentid=document.id
where (documenttriple.[predicate]='somepredicate' and documenttriple.[object]='someobject')
or (documenttriple.[predicate]='somepredicate')

如何在 EF 代码中执行此操作?我试过了:

documents = from d in documents
    join dt in db.DocumentTriples on d.Id equals dt.DocumentId
    where filter.Triples.Any(t => t.Item1 == dt.Predicate &&
        (t.Item2 == null || t.Item2 == dt.Object))
    select d;

但这似乎并没有过滤三元组。

编辑

我尝试使用更多代码示例更好地解释自己。这是我的(缩短的)代码:

private IQueryable<Document> ApplyDocumentFilters(IQueryable<Document> documents, 
    DocumentFilter filter)
{
    if (!String.IsNullOrEmpty(filter.CreatorId))
        documents = documents.Where(d => d.CreatorId == filter.CreatorId);
    if (!String.IsNullOrEmpty(filter.CategoryId))
        documents = documents.Where(d => d.CategoryId == filter.CategoryId);
    if (!String.IsNullOrEmpty(filter.Title))
        documents = documents.Where(d => d.Title.Contains(filter.Title));
    // ...and so forth for all the filter properties...
    // here I want to filter only the documents which have at least 1 of their triples
    // matching any of the filter's triples. DOES NOT WORK
    if (filter.Triples.Count > 0)
    {
        documents = from d in documents
                    join dt in db.DocumentTriples on d.Id equals dt.DocumentId
                    where filter.Triples.Any(t => t.Item1 == dt.Predicate &&
                                                  (t.Item2 == null || t.Item2 == dt.Object))
                    select d;
    }
    return documents;
}

public PagedData<Document> GetDocuments(DocumentFilter filter)
{
    if (filter == null) throw new ArgumentNullException(nameof(filter));
    using (CatalogContext db = new CatalogContext(_options))
    {
        IQueryable<Document> documents = db.Documents
                        .Include(d => d.DocumentTriples)
                        .AsNoTracking().AsQueryable();
        documents = ApplyDocumentFilters(documents, filter);
        int total = documents.Count();
        documents = documents.OrderBy(d => d.Title).ThenBy(d => d.PublicationYear);
        return new PagedData<Document>(total,
            documents.Skip((filter.PageNumber - 1) * filter.PageSize).Take(filter.PageSize).ToList());
    }
}

如您所见,我收到一个过滤器对象,其中包含我要应用的所有过滤器,然后按属性构建 IQueryable 属性。至于三元组,我希望所有文档至少有 1 个与过滤器的任何三元组匹配的三元组。

【问题讨论】:

    标签: c# entity-framework .net-core


    【解决方案1】:

    你在 Linq 中的 SQL 查询是

    var i = from x in Db.Documents // SELECT * FROM Document
            join y in Db.DcumentTriple // JOIN DocumentTriples 
            on x.Id equals y.DocumentId // ON DocumentId = DocumentId
            where (y.Predicate.Equals("somepredicate") && // WHERE ( DocumentTriple.[Predicate] = 'somepredicate' &&
            y.Object.Equals("someobject")) || // DocumentTriple.[Object] = 'someobject') ||
            y.Predicate.Equals("somepredict") // DocumentTriple.[Predicate] = 'somepredicate'
            select new { x, y };
    

    但是你不必像那样走很长的路(我只是翻译了查询,That Query is a Bad Query),它可以简单地做到:

    var i = Db.DocumenTriple.Where(x=> (x.Predicate.Equals("something") && x.Object.Equals("something")) || x.Predicate.Equals("something");
    

    这将为您延迟加载Db.Document 导航属性。如果您想急切加载,请在其末尾调用.Include(x=&gt; x.Document)

    更新

    正如你在评论中提到的,如果我没记错的话,你有很多 where 声明。在这种情况下,以下是可能的:

    public IEnumerable<Expression<Func<TestEntity, Boolean>>> filters = new List<Expression<Func<TestEntity, Boolean>>>() { };
    public IQueryable<TestEntity> filter(IQueryable<TestEntity> input, IEnumerable<Expression<Func<TestEntity, Boolean>>> filters, int i) {
            if (i == 0)
                return input;
            return filter(input.Where(filters.ElementAt(i)), filters, i - 1);
    }
    

    请注意,此方法中过滤器的顺序很重要。如果您不希望这样做,可以使用foreach 循环和concat 将结果添加到另一个列表。

    【讨论】:

    • 谢谢,如果我没有很好地解释我自己,那我很糟糕,但是我有一个表示过滤器的元组数组,并且我在同一个 IQueryable 上连接越来越多的条件。 SQL 示例旨在显示过滤的类型,而不是在我的场景中代码获取一个数组并且匹配其中的任何元组就足以通过的事实。最后,我必须有一个 IQueryable 包括他们的三元组;我从 db.Documents.Include(d => d.DocumentTriples).AsNoTracking().AsQueryable() 开始,向其中添加一些过滤器,最后对它进行排序和分页。
    • 好的,我仍然对此感到困惑,但如果我正确理解了问题,您可以按照我在更新的答案中添加的内容进行操作。
    • 我刚刚通过添加一些具体代码来编辑问题。这应该使我的问题更清楚。谢谢
    猜你喜欢
    • 1970-01-01
    • 2021-05-25
    • 2016-05-26
    • 2012-10-27
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多