如何搜索来自另一个页面模型的串联名称列表?

Rob*_*b C 5 c# asp.net-core razor-pages

我的项目中有多个模型,但在下面的屏幕中,大多数字段/属性都位于 SecurityLog 模型中。

下面是我显示连接的军官列表的地方。除了官员姓名之外,我的搜索和列标题排序可以正常工作。我很难合并官员姓名 b/c 列表来自另一个页面模型。

在此处输入图片说明

这是我的数据库架构和示例结果

在此处输入图片说明

我可以根据 Microsoft 的 Contoso 大学的演示来实现排序、搜索和分页。

https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-3.1

我如何解决在我当前代码中搜索官员姓名的问题?更具体地说,对于搜索……我如何阅读(迭代)OfficerID 列表并搜索每个列表项(连接的官员列表行)的字符串值?

foreach (SecurityLog secLog in SecurityLog)
        {
            secLogCopy = secLog;

            OfficerLists = officerList.GetOfficerList(_context, secLog, rowID, OfficerIDs);
            if (!String.IsNullOrEmpty(searchString))
            {
                sort = sort.Where(s => OfficerIDs.ToString().Contains(searchString));
            }
            rowID++;
        }
Run Code Online (Sandbox Code Playgroud)

页面模型:

namespace SecurityCore.Pages.SecurityLogs
{
    public class IndexModel : PageModel
    {
        private readonly SecurityCore.Models.SecurityCoreContext _context;

        public IndexModel(SecurityCore.Models.SecurityCoreContext context)
        {
            _context = context;
        }

        public string EventDateSort { get; set; }        
        public string CurrentSort { get; set; }


        [DataType(DataType.Date)]
        public Nullable<DateTime> DateEnd { get; set; }
        [DataType(DataType.Date)]
        public Nullable<DateTime> DateBegin { get; set; }
        public Entity Entity { get; set; }


        public PaginatedList<SecurityLog> SecurityLog { get; set; }
        public List<secLog> SecurityLogOfficers { get; set; } = new List<secLog>();
        public List<string> OfficerLists { get; set; }

        [BindProperty]
        public OfficerList officerList { get; set; } = new OfficerList();
        [BindProperty]
        public List<string> OfficerIDs { get; set; } = new List<string>();







    public async Task OnGetAsync(string sortOrder, string currentFilter, string searchString, int? pageIndex,
                                 string entitySelect, string entityFilter, DateTime dateBegin, DateTime dateBeginSelect, DateTime dateEnd, DateTime dateEndSelect)
    {
        selectedEntity = new SelectList(_context.Entity.Where(a => a.Active == "Y"), "Name", "Name");

        CurrentSort = sortOrder;
        EventDateSort = sortOrder == "EventDate" ? "EventDate_Desc" : "EventDate";            
        OfficerNameSort = sortOrder == "OfficerName" ? "OfficerName_Desc" : "OfficerName";


        IQueryable<SecurityLog> sort = from s in _context.SecurityLog select s;


        switch (sortOrder)
        {
            case "EventDate":
                sort = sort.OrderBy(s => s.EventDate);
                break;                                
            case "OfficerName":                    
                sort = sort.OrderBy(s => officerList.ToString()).ThenBy(s => s.EventDate);
                break;
            case "OfficerName_Desc":                    
                sort = sort.OrderByDescending(s => officerList.ToString()).ThenBy(s => s.EventDate);
                break;
            default:
                sort = sort.OrderByDescending(s => s.EventDate);
                break;
        }

        int pageSize = 5;





        SecurityLog = await PaginatedList<SecurityLog>.CreateAsync(sort
        .Include(a => a.Entity)
        .Include(b => b.EventType)
        .Include(c => c.Location)
        .Include(d => d.ShiftRange)
        .Include(e => e.Officer)                                    
        .AsNoTracking(), pageIndex ?? 1, pageSize);



        int rowID;
        rowID = 0;


        foreach (SecurityLog secLog in SecurityLog)
        {
            secLogCopy = secLog;
            OfficerLists = officerList.GetOfficerList(_context, secLog, rowID, OfficerIDs);
            if (!String.IsNullOrEmpty(searchString))
            {
                sort = sort.Where(s => OfficerIDs.ToString().Contains(searchString));
            }
            rowID++;
        }



        if (!String.IsNullOrEmpty(searchString))
        {                                                

            sort = sort.Where(s => s.Narrative.Contains(searchString)                                    
                                || s.ContactName.Contains(searchString)
                                || s.SubjectFirst.Contains(searchString)
                                || s.SubjectLast.Contains(searchString));                                    
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

}

官员名单.cs

public class OfficerList
{
    public List<string> GetOfficerList(SecurityCoreContext _context, SecurityLog secLog, int rowID, List<string> OfficerIDs)
    {            

        int CurrentID = secLog.ID;

        var SecLogOfficer = _context.SecurityLogOfficer.ToList();
        var Officer = _context.Officer.ToList();


        int count = SecLogOfficer.Where(slo => slo.SecurityLogID == CurrentID).Count();

        if (count >= 0)
        {
            OfficerIDs.Add("");
        }
        foreach (secLog slo in SecLogOfficer.Where(slo => slo.SecurityLogID == CurrentID))
        {
            OfficerIDs[rowID] = OfficerIDs[rowID] + slo.Officer.FullName + ", ";
        }
        if (count > 0)
        {
            OfficerIDs[rowID] = OfficerIDs[rowID].Substring(0, OfficerIDs[rowID].Length - 2);
        }


        return OfficerIDs;

    }

}
Run Code Online (Sandbox Code Playgroud)

页:

@page
@model WebApplication_core_razorpage.Pages.HomeModel
@{
    ViewData["Title"] = "Home";
    Layout = "~/Pages/Shared/_Layout.cshtml";
    var i = 0;
}

<h1>Home</h1>

<table>
    @foreach (var item in Model.SecurityLog)
    {
        <tr>
            <td style="width:4% !important">
                @Html.DisplayFor(modelItem => item.ID)
            </td>
            <td style="width:5% !important">
                @Html.DisplayFor(modelItem => item.EventDate)
            </td>

            <td style="width:5% !important">
                @Model.OfficerLists[i]
            </td>
        </tr>
        i++;
    }

</table>
Run Code Online (Sandbox Code Playgroud)

分页列表.cs

public class PaginatedList<T> : List<T>
{
    public int PageIndex { get; private set; }
    public int TotalPages { get; private set; }        

    public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);

        this.AddRange(items);
    }

    public bool HasPreviousPage
    {
        get
        {
            return (PageIndex > 1);
        }
    }


    public bool HasNextPage => PageIndex < TotalPages;

    public bool ShowFirst
    {
        get
        {
            return (PageIndex != 1);
        }
    }

    public bool ShowLast
    {
        get
        {
            return (PageIndex != TotalPages);
        }
    }

    public static async Task<PaginatedList<T>> CreateAsync(
        IQueryable<T> source, int pageIndex, int pageSize)
    {
        var count = await source.CountAsync();
        var items = await source.Skip(
            (pageIndex - 1) * pageSize)
            .Take(pageSize).ToListAsync();
        return new PaginatedList<T>(items, count, pageIndex, pageSize);
    }
}
Run Code Online (Sandbox Code Playgroud)

安全日志文件

namespace SecurityCore.Models
{
public class SecurityLog
{                

    [BindProperty(SupportsGet = true)]
    public int ID { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
    [Display(Name = "Event Date")]
    public System.DateTime EventDate { get; set; }

    public virtual Officer Officer { get; set; }        
    public virtual List<secLog> SecurityLogOfficers { get; set; }       


  }
}
Run Code Online (Sandbox Code Playgroud)

关系

public class SecurityCoreContext : DbContext
{
    public SecurityCoreContext (DbContextOptions<SecurityCoreContext> options)
        : base(options)
    {
    }

    public DbSet<SecurityCore.Models.SecurityLog> SecurityLog { get; set; }        

    public DbSet<SecurityCore.Models.secLog> SecurityLogOfficer { get; set; }

    public DbSet<SecurityCore.Models.Officer> Officer { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<secLog>()
            .HasKey(t => new { t.SecurityLogID, t.OfficerID });

        modelBuilder.Entity<secLog>()
            .HasOne(pt => pt.SecurityLog)
            .WithMany(p => p.SecurityLogOfficers)
            .HasForeignKey(pt => pt.SecurityLogID);

        modelBuilder.Entity<secLog>()
            .HasOne(pt => pt.Officer)
            .WithMany(t => t.SecurityLogOfficers)
            .HasForeignKey(pt => pt.OfficerID);
    }

}
Run Code Online (Sandbox Code Playgroud)

Rez*_*aei 5

基于多对多关系的搜索

说到文章和作者,当每篇文章可能有很多作者时,假设您要根据 a 进行搜索term并找到文章名称或文章摘要中包含该术语的文章,或者该文章的作者之一在其名称中包含该术语名字或他们的姓氏。

EF 6 - 多对五,没有关系的实体类

您可以使用 处理 Linq 查询中的这些情况,与使用Any处理 SQL 查询的方式相同EXISTS:

Where(article=> article.Title.Contains(term) || 
                article.Abstract.Contains(term) || 
                article.Authors.Any(author => 
                    author.FirstName.Contains(term) ||
                    author.LastName.Contains(searchTerm)))
Run Code Online (Sandbox Code Playgroud)

它并不完全生成以下 SQL 查询,但其逻辑与 SQL 中的以下内容非常相似:

FROM Articles
WHERE (Articles.Title LIKE '%' + @Term + '%') OR 
      (Articles.Abstract LIKE '%' + @Term + '%') OR 
      EXISTS (SELECT * FROM Authors 
              WHERE (Authors.FirstName LIKE '%' + @Term + '%') OR
                    (Authors.LastName LIKE '%' + @Term + '%'))
Run Code Online (Sandbox Code Playgroud)

EF CORE - Many-To-May 与关系的实体类

目前,尚不支持没有实体类来表示连接表的多对多关系。

您可以在 Linq 查询中使用 处理这些情况Any,与使用EXISTS+在 SQL 查询中处理的方式相同Join:

.Where(article => article.Title.Contains(model.SearchTerm) ||
                  article.Abstract.Contains(model.SearchTerm) ||
                  article.ArticlesAuthors.Any(au =>
                      (au.Author.FirstName).Contains(model.SearchTerm) ||
                      (au.Author.LastName).Contains(model.SearchTerm)))
Run Code Online (Sandbox Code Playgroud)

它并不完全生成以下 SQL 查询,但其逻辑与 SQL 中的以下内容非常相似:

FROM Articles
WHERE (Articles.Title LIKE '%' + @Term + '%') OR 
      (Articles.Abstract LIKE '%' + @Term + '%') OR 
      EXISTS (SELECT * FROM ArticlesAuthors 
              INNER JOIN Authors 
              ON ArticlesAuthors.AuthorId = Authors.Id
              WHERE ((Authors.FirstName LIKE '%' + @Term + '%') OR
                     (Authors.LastName LIKE '%'+ @Term + '%')) AND 
                     (Articles.Id = ArticlesAuthors.ArticleId))
Run Code Online (Sandbox Code Playgroud)

EF 6 - 示例

问题有点混乱,包括搜索排序和大量代码,需要更多关注。为了使它对您和功能读者更有用和更容易理解,我将使用一个更简单的模型,它的属性更少,更容易理解。

正如您在 EF 图中所见,该ArticlesAuthors表并未显示在图中,因为它是一个多对多关系,仅包含其他实体的 Id 列,没有任何额外字段

在此处输入图片说明

搜索逻辑

我们要根据找到的文章SerachTerm,PublishDateFrom以及PublishDateTo:

  • 如果文章的标题或摘要包含该术语,则文章应该是结果的一部分。
  • 如果文章作者的名字和姓氏组合包含该术语,则文章应该是结果的一部分。
  • 如果发布日期大于或等于PublishDateFrom,文章应该是结果的一部分,如果发布日期小于或等于PublishDateTo,文章应该是结果的一部分。

这是一个搜索模型:

public class ArticlesSearchModel
{
    public string SearchTerm { get; set; }
    public DateTime? PublishDateFrom { get; set; }
    public DateTime? PublishDateTo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

下面是搜索代码:

请注意:Inculde与搜索无关,仅用于在输出结果中包含相关实体。

public class ArticlesBusinessLogic
{
    public IEnumerable<Article> Search(ArticlesSearchModel model)
    {
        using (var db = new ArticlesDBEntities())
        {
            var result = db.Articles.Include(x => x.Authors).AsQueryable();

            if (model == null)
                return result.ToList();

            if (!string.IsNullOrEmpty(model.SearchTerm))
                result = result.Where(article => (
                    article.Title.Contains(model.SearchTerm) ||
                    article.Abstract.Contains(model.SearchTerm) ||
                    article.Authors.Any(author =>
                    (author.FirstName + " " + author.LastName).Contains(model.SearchTerm))
                ));

            if (model.PublishDateFrom.HasValue)
                result = result.Where(x => x.PublishDate >= model.PublishDateFrom);

            if (model.PublishDateFrom.HasValue)
                result = result.Where(x => x.PublishDate <= model.PublishDateTo);

            return result.ToList();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

EF 核心 - 示例

正如我上面提到的,目前尚不支持没有实体类来表示连接表的多对多关系,因此使用 EF CORE 的模型将是:

在此处输入图片说明

下面是搜索代码:

请注意:Inculde与搜索无关,仅用于在输出结果中包含相关实体。

public IEnumerable<Article> Search(ArticlesSearchModel model)
{
    using (var db = new ArticlesDbContext())
    {
        var result = db.Articles.Include(x=>x.ArticleAuthor)
                                .ThenInclude(x=>x.Author)
                                .AsQueryable();

        if (model == null)
            return result;

        if (!string.IsNullOrEmpty(model.SearchTerm))
        {
            result = result.Where(article => (
                article.Title.Contains(model.SearchTerm) ||
                article.Abstract.Contains(model.SearchTerm) ||
                article.ArticleAuthor.Any(au =>
                    (au.Author.FirstName + " " + au.Author.LastName)
                        .Contains(model.SearchTerm))
            ));
        }
        if (model.PublishDateFrom.HasValue)
        {
            result = result.Where(x => x.PublishDate >= model.PublishDateFrom);
        }
        if (model.PublishDateFrom.HasValue)
        {
            result = result.Where(x => x.PublishDate <= model.PublishDateTo);
        }

        return result.ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ali*_*eza 1

你做错了很多事情:

  1. 您不能.ToString()在类或列表上使用。所以首先你必须删除或更改这些行。例如 :
  sort = sort.Where(s => OfficerIDs.ToString().Contains(searchString));

  sort = sort.OrderBy(s => officerList.ToString()).ThenBy(s => s.EventDate);

  sort = sort.OrderByDescending(s => officerList.ToString()).ThenBy(s => s.EventDate);
Run Code Online (Sandbox Code Playgroud)
  1. 每次加载页面或更改搜索或排序时,您几乎都会从数据库表中加载整个数据。当然,有了分页,这个问题就变得有点模糊了

  2. 您没有使用实体框架来加载关系数据,因此您无法编写加载您需要的内容或用户搜索的内容的查询。(您正在从数据库的不同部分获取数据)


我知道这不是你想要的,但老实说,我试图回答你的问题并帮助你解决问题,但我最终又重写了整个事情......你应该将你的问题分解成更小的部分并提出一个更概念性的问题。