+-
LINQ查询连接两个不同的表

我目前正在做一个图书馆管理项目,当用户借书时,会更新他们的阅读状态。当我使用linq来连接表时,除了状态之外,其他的都在那里.下面是我的模型:User模型(TblUser)。

using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;

namespace LibMan.Models.DB
{
    public partial class TblBookStatus
    {
        public int ResId { get; set; }
        public string UserEmail { get; set; }
        public int? BookId { get; set; }
        public string Status { get; set; }

        public virtual TblBook Book { get; set; }
        public virtual TblUser User { get; set; }
    }

}

Book Model(TblBook):

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace LibMan.Models.DB
{
    public partial class TblBook
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Translator { get; set; }
        public string Publisher { get; set; }
        public string Description { get; set; }
        public string Category { get; set; }
        public byte[] Cover { get; set; }
    }
}

图书状态模型(TblBookStatus)。

using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;

namespace LibMan.Models.DB
{
    public partial class TblBookStatus
    {
        public int ResId { get; set; }
        public string UserEmail { get; set; }
        public int? BookId { get; set; }
        public string Status { get; set; }

        public virtual TblBook Book { get; set; }
        public virtual TblUser User { get; set; }
    }

}

下面是我的代码,你可以看到我用linq查询连接创建了DisplayBook,但我不能在html端显示状态。它说它

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LibMan.Models.DB;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;

namespace LibMan.Pages
{
    public class LibraryModel : PageModel
    {
        private readonly LibMan.Models.DB.LibManContext _context;
        public string Message { get; set; }
        public LibraryModel(LibMan.Models.DB.LibManContext context)
        {
            _context = context;
            DisplayBook = new List<TblBook>();
;       }
        public IList<TblBookStatus> TblBookStatus { get; set; }
        public IList<TblBook> TblBook { get; set; }
        public IList<TblBook> DisplayBook { get; set; }
        public IList<TblUser> TblUser{ get; set; }
        [BindProperty]
        public async Task OnGetAsync()
        {
            TblBookStatus = await _context.TblBookStatus.ToListAsync();
            TblBook = await _context.TblBook.ToListAsync();
            TblUser = await _context.TblUser.ToListAsync();
            var UserEmail = HttpContext.Session.GetString("Email");

            if (TblBookStatus != null && TblBook != null)
            {
                DisplayBook = (from book in TblBook
                                join stat in TblBookStatus
                                on book.BookId equals stat.BookId
                                join usr in TblUser
                                on stat.UserEmail equals usr.Email
                                where (usr.Email == UserEmail)
                                select book).ToList();
            }
        }
    }
}

我如何才能做到这一点?我可以在将DisplayBook分配给Linq查询结果的语句中这样做吗?最好的方法是什么?谢谢!注:如果需要,我可以提供html代码。

1
投票

有多种选择。你可以纠正你的代码,改变类型的 DisplayBook 到valuetle(或创建单独的类型,以便于阅读)。

 public IList<(TblBook Book, TblBookStatus  Status)> DisplayBook { get; set; }

与用户取书和状态。

TblBookStatus = await _context.TblBookStatus.Include(s => s.User).ToListAsync();
TblBook = await _context.TblBook.ToListAsync();

最后填写 DisplayBook:

DisplayBook = TblBook
    .Select(b => (b, TblBookStatus.FirstOrDefault(s => s.BookId == b.BookId)))
    .ToList()

据我从你的评论中了解到,你的状态和用户之间是一对一的关系,所以这样你就可以获取你的图书馆中的所有书籍和所有状态(书籍的状态可以是 null)与填充用户关系。

P.S.

ToListAsync 不应该返回null,所以null检查是多余的。

UPD

要只获取一个用户和他的书的信息,你可以用单一查询来实现。

var statusesWithUserAndBooks = await _context.TblBookStatus
    .Include(s => s.User)
    .Include(s => s.Book)
    .Where(s => s.User.Email == UserEmail)
    .ToListAsync();

如果你需要一个数据的子集,那么就可以创建 DTO 与必要的字段,像这样。

public class DisplayBookDTO
{
    public string Status { get; set; }
    public string BookTitle { get; set; }
    public string BookAuthor { get; set; }
}

public IList<DisplayBookDTO> DisplayBook { get; set; }

DisplayBook = await _context.TblBookStatus
    .Where(s => s.User.Email == UserEmail
        && s.Book != null)
    .Select(s => new DisplayBookDTO
    {
        Status = s.Status,
        BookTitle = s.Book.Title,
        BookAuthor = s.Book.Author
    })
    .ToListAsync();
1
投票

不需要分别查询每个表,然后连接。 让LINQ为你做。

var UserEmail = HttpContext.Session.GetString("Email");

var books = _context.TblBookStatus
     .Where(
        tbs => (tbs.Book != null && tbs.BookId == tbs.Book.BookId) &&
               (tbs.User != null && tbs.UserEmail == tbs.User.Email) &&
               tbs.UserEmail == UserEmail)
     .Select(tbs => tbs.Book);

因为你在EF模型中定义了导航属性(使用虚拟属性),所以连接将自动为你完成。 在上面的例子中,我是设置tbs.BookId == tbs.Book.BookId。 如果你在实体类型配置中定义了外键,就不需要这样做。 你也可以在你的EF模型中使用属性来定义这些关系。

例子:你可以在你的EF模型中使用属性来定义这些关系。

[ForeignKey("BookId")]
public virtual TblBook Book { get; set; }

EDIT:分解你需要返回的内容。

var books = _context.TblBookStatus
         .Where(
            tbs => (tbs.Book != null && tbs.BookId == tbs.Book.BookId) &&
                   (tbs.User != null && tbs.UserEmail == tbs.User.Email) &&
                   tbs.UserEmail == UserEmail)
         .Select(tbs => new {
                   Status = tbs.Status,
                   BookName = tbs.Book.Name
                             });