从SQL到IQueryable LINQ

5

你好,我正在尝试将以下SQL语句转换为LINQ等价语句。由于我对.NET(编程仅一天)非常陌生,所以我已经卡了几个小时了。

SELECT * 
 FROM Books 
 WHERE BookID IN (SELECT BookID 
              FROM Borrows 
              WHERE UserID = 2) 

这是模型。
public class LibUser
{       
    [Key]
    public int UserID { get; set; }
    [Required, StringLength(50), Display(Name = "First Name")]
    public string UserFirstName { get; set; }
    [Required, StringLength(50), Display(Name = "Last Name")]
    public string UserLastName { get; set; }
    [Required, StringLength(10000), Display(Name = "Residence"), DataType(DataType.MultilineText)]
    public string Adress { get; set; }
}

public class Book { 
    [Key]
    public int BookID { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public DateTime Published{ get; set; }
}

public class Borrowed {
    [Key]
    public int BorrowID { get; set; }
    public int UserID { get; set; }
    public int BookID { get; set; } 
}

我非常感谢任何人的帮助。

编辑

上下文类

public class LibraryContext : DbContext
{

          public LibraryContext()
        : base("libromatic")
    {
    }

    public DbSet<LibUser> LibUsers { get; set; }
    public DbSet<Book> Books { get; set; }
    public DbSet<Borrowed> Borrows { get; set; }

}

无论您是否在使用它,请避免使用“Select *”。 - bumble_bee_tuna
@bumble_bee_tuna,是的,我意识到我只是为了简单起见在解决LINQ时使用了那个查询。 - Pavel Kašelják
5个回答

4

最好使用连接(join)来实现。

原因是:如果用户借了大量的书籍,或者数据存在错误,则子查询可能会返回大量的ID,在长列表上使用SQL的“IN”语句会变得非常缓慢。

使用连接(join):

SQL查询语句:

SELECT Books.* FROM Books 
  JOIN Borrows ON Borrows.BookID = Books.BookID
  WHERE Borrows.UserID = 2 

Linq语句:

var allBooksBorrowedByUser2 = db.Borrowed
                .Where(borrow => borrow.UserID == 2)
                .Join(db.Books,
                      borrow => borrow.BookID,
                      book => book.BookID,
                      (borrow, book) => book);

在平均情况下,这可能是一个性能更好的查询。 - Alex

4
假设您的上下文被称为 db,您可以执行以下查询。
var borrowedBooksForUser = db.Books
     .Where(b => db.Borrowed.Any(x => x.UserID == 2 && x.BookID == b.BookID));

这会抛出 System.InvalidOperationException 异常,指出“'LibraryContext'上下文的模型已经发生了改变,自从创建数据库以来。”老实说,我不知道它为什么会这样做。 - Pavel Kašelják
@PavelKašelják 看起来您正在使用实体框架,而不是您的问题所建议的LINQ to SQL。此外,似乎您在代码中进行了模式更改,但尚未与现有数据库内容匹配。请参考以下链接以获取更多信息:https://dev59.com/_mEh5IYBdhLWcg3w7nSW 和 http://forums.asp.net/t/1998436.aspx?model+backing+the+context+has+changed+since+the+database+was+created+Consider+using+Code+First+Migrations+to+update+the+database。 - Alex
这个答案绝对是以最简单的方式获得与问题中所设定目标非常相似的SQL的方法。 - Arkaine55
@Arkaine55,没错,虽然在书籍 id 上使用连接条件可能会产生其他可能的查询,但最终结果是相同的。 - Alex

3
你可以像这样做:

你可以这样做:

var Lnq = new LinqDataContext();
var borrowId = Lnq.Borrowed.Where(a => a.UserID == 2).Select(a => a.BookID).ToList();

var bookQuery = Lnq.Books.Where(a => borrowId.Contains(a.BookID))
                         .Select(a => a.YourColumn);

Contains()之前,它抱怨BookID在当前上下文中不存在。 - Pavel Kašelják
@PavelKašelják 请查看我所做的编辑,添加.Select(a => a.BookId) - bumble_bee_tuna
我已经这样做了,但它仍然抱怨这一行中的 BookIDLnq.Books.Where(a => BookID.Contains。我在我的问题中添加了上下文类。 - Pavel Kašelják
1
最近的编辑使得这实际上变成了两个单独执行的查询,第二个查询本质上将("SELECT BookID FROM Borrows WHERE UserID = 2")的结果作为复杂的where子句传递进去。 - Arkaine55
@Arkaine55 是的,我试图让他更容易理解。 - bumble_bee_tuna

3
导航会使一切变得更简单。
public class Borrowed {
    [Key]
    public int BorrowID { get; set; }
    public int UserID { get; set; }
    public int BookID { get; set; } 

    // Navigation Properties
    public virtual LibUser User { get; set; }
    public virtual Book Book { get; set; }
}

Borrows.Where(borrow => borrow.UserId == 2)
       .Select(borrow => borrow.Book);

这个解决方案只是选择了Borrows表内的内容,对吧?我需要根据用户所借阅的书籍来获取书籍详细信息,因此我需要连接这两个表。然后返回IQueryable<Book>而不是IQueryable<Borrowed> - Pavel Kašelják
抱歉,我在那里犯了一个小错误,它不是SelectMany而是Select。否则,它会将书籍投影出来...正如您首先注意到的,我们首先搜索给定用户ID借阅的书籍...然后一旦搜索缩小到所有书籍,它将返回IQueryable<Borrowed>,除了Select语句之外,它将“投影”实际的“Book”。 - Aydin

2

试试这个:

  var getResult=from b in db.Books
  join bo in db.Borrows on b.BookID=bo.BookID
  where bo.UserID=2 

可以解释一下这是什么,以及为什么吗? - endrik exe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接