(已编辑)我正在使用文档表和文档三元组表来试验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
对象,包括三元组,然后添加越来越多像documents = documents.Where(...)
这样的过滤器。
其中一个过滤器是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属性。至于三元组,我希望得到的所有文件至少有一个三元组,它与任何过滤器的三元组相匹配。
答案 0 :(得分:0)
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 };
但是你不必那么长时间(我只是翻译了查询,那个查询是一个错误的查询),它可以简单地完成:
var i = Db.DocumenTriple.Where(x=> (x.Predicate.Equals("something") && x.Object.Equals("something")) || x.Predicate.Equals("something");
这将为您延迟加载Db.Document
导航属性。如果您想要加载它,请在结尾处拨打.Include(x=> 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
将结果导入另一个名单。