我在两个实体之间具有一对多关系,并且需要查询这两个实体的组合。
这是一个File模型:
public class FileMetadata
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string UserName { get; set; }
public DateTime UploadDate { get; set; }
public int FileContentId { get; set; }
public FileContent Content { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
另一个是Tag模型:
public class FileTag
{
[Key]
public int Id { get; set; }
public string Text { get; set; }
public int FileMetadataId { get; set; }
public FileMetadata Metadata { get; set; }
}
关系的本质是,一个文件可以有一对多的标签。我需要做的是实现一个搜索方法,它可以搜索文件的内容(描述、名称等),但是也,搜索该文件的所有标签。这就是问题所在,我找不到一种高效的方法来做到这一点。
这是我到目前为止所做的,它给了我预期的结果,但是,效率不高:
public async Task<ServiceResponse<FileSearchResult>> SearchFileMetaData(string searchText, int page, float resultsPerPage)
{
var pageCount = Math.Ceiling((await FindFilesBySearchText(searchText)).Count / resultsPerPage);
//Add tags to descriptions
var extendedFiles = _context.Files.ToList();
var tags = _context.FileTags.ToList();
foreach (var file in extendedFiles)
{
var thisFilesTags = tags.Where(t => t.FileMetadataId == file.Id).ToList();
foreach (var tag in thisFilesTags)
{
file.Description += tag.Text;
}
}
//Search files with metadata matching the search text.
var files = extendedFiles
.Where(f => f.Name.ToLower().Contains(searchText.ToLower())
|| f.Description.ToLower().Contains(searchText.ToLower()))
.Skip((page - 1) * (int)resultsPerPage)
.Take((int)resultsPerPage)
.ToList();
var response = new ServiceResponse<FileSearchResult>
{
Data = new FileSearchResult
{
Files = files,
ThisPageNumber = page,
TotalPages = (int)Math.Floor(pageCount)
}
};
return response;
}
我从数据库中获取所有文件和标签,循环使用该文件实体的标签构建描述字段,然后执行 Linq 操作来过滤搜索文本。
怎样才能仍然得到预期的结果,但提高性能,但又不会得到不必要的记录?
此外,是否可以通过连接这两个实体的更好的 Linq 查询来解决这个问题?或者,关系/表是否需要重新定义?
尝试以下查询。它尝试在实现之前过滤掉文件。当然这不是理想的解决方案,
Contains
执行全表扫描,如果表很大,这可能会影响性能。
public async Task<ServiceResponse<FileSearchResult>> SearchFileMetaData(string searchText, int page, float resultsPerPage)
{
searchText = searchText.ToLower();
var filesQuery = _context.Files
.Where(f => f.Name.ToLower().Contains(searchText)
|| f.Description.ToLower().Contains(searchText)
|| _context.FileTags.Any(t => t.FileMetadataId == f.Id && t.Text.ToLower().Contains(searchText))
);
var pageCount = Math.Ceiling((await filesQuery.CountAsync()) / resultsPerPage);
var filesQuery = filesQuery
.Skip((page - 1) * (int)resultsPerPage)
.Take((int)resultsPerPage)
.AsNoTracking()
.ToList();
var response = new ServiceResponse<FileSearchResult>
{
Data = new FileSearchResult
{
Files = files,
ThisPageNumber = page,
TotalPages = (int)Math.Floor(pageCount)
}
};
return response;
}