2016-10-04 6 views
2

Я загружаю IQueryable книг из таблицы Books, и есть другая таблица с именем BookLoans. У меня есть DateTime? свойство в моей ViewModel с именем Availability. Я пытаюсь выяснить, как лучше всего выйти во время моего оператора select и посмотреть, появляется ли текущая книга BookID в записи в таблице BookLoans и если DateTime? переменная с именем ReturnedWhen также имеет значение NULL в этой записи. В основном я пытаюсь пометить книгу как доступную или проверенную, когда я заполняю IQueryable.Проверьте значение из второй таблицы в операторе LINQ

Было высказано предположение, что я пытаюсь сделать все это как один оператор LINQ, но я пытаюсь выяснить, будет ли проще просто заполнять книги следующим образом, а затем запускать их через цикл foreach? Также было предложено, что Join будет работать, но на самом деле я просто пытаюсь найти лучший метод, а затем, как это сделать.

var books = 
_context.Books 
.Select(r => new BookIndexViewModel 
{ 
    BookID = r.BookID, 
    Title = r.Title, 
    Author = r.Author, 
    ISBN = r.ISBN, 
    GenreName = r.Genre.Name 
}).ToList(); 

Книга Entity:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

namespace Open_School_Library.Data.Entities 
{ 
    public class Book 
    { 
     public int BookID { get; set; } 
     public string Title { get; set; } 
     public string SubTitle { get; set; } 
     public string Author { get; set; } 
     public int GenreID { get; set; } 
     public int DeweyID { get; set; } 
     public int ISBN { get; set; } 

     public Genre Genre { get; set; } 
     public Dewey Dewey { get; set; } 
     public virtual BookLoan BookLoan { get; set; } 
    } 
} 

BookLoan Entity:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

namespace Open_School_Library.Data.Entities 
{ 
    public class BookLoan 
    { 
     public int BookLoanID { get; set; } 
     public int BookID { get; set; } 
     public int StudentID { get; set; } 
     public DateTime CheckedOutWhen { get; set; } 
     public DateTime DueWhen { get; set; } 
     public DateTime? ReturnedWhen { get; set; } 

     public Book Book { get; set; } 
     public Student Student { get; set; } 
    } 
} 

BookIndexViewModel:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.Linq; 
using System.Threading.Tasks; 

namespace Open_School_Library.Models.BookViewModels 
{ 
    public class BookIndexViewModel 
    { 
     public int BookID { get; set; } 
     public string Title { get; set; } 
     [Display(Name = "Author")] 
     public string Author { get; set; } 
     [Display(Name = "Genre")] 
     public string GenreName { get; set; } 
     public int ISBN { get; set; } 
     public string BookLoan { get; set; } 
     public DateTime? Availability { get; set; } 
    } 
} 

ответ

1

Вот некоторые упрощенные версии ваших классов

public class Book { 
    public int BookId { get; set; } 
    public string Title { get; set; } 
} 

public class BookLoan { 
    public int BookId { get; set; } 
    public DateTime CheckedOutOn { get; set; } 
    public DateTime? ReturnedOn { get; set; } 
    public DateTime DueOn { get; set; } 
} 

public class BookViewModel { 
    public int BookId { get; set; } 
    public string Title { get; set; } 
    public bool IsAvailable { get; set; } 
    public DateTime? AvailableOn { get; set; } 
} 

Тогда мы можем сделать две вещи, чтобы получить список вид модели вы ищете:

  1. Посмотрите только книги кредитов, ReturnedOn является нулевой
  2. Используйте DefaultIfEmpty сделать левое внешнее соединение см https://msdn.microsoft.com/en-us/library/bb397895.aspx

Таким образом, вы в конечном итоге с одного пункта на книгу и только loanWithDefault если книга в настоящее время проверены.

var viewModel = from book in books 
      join loan in loans.Where(x => !x.ReturnedOn.HasValue) on book.BookId equals loan.BookId into result 
      from loanWithDefault in result.DefaultIfEmpty() 
      select new BookViewModel { 
       BookId = book.BookId, 
       Title = book.Title, 
       IsAvailable = loanWithDefault == null, 
       AvailableOn = loanWithDefault == null ? (DateTime?) null : loanWithDefault.DueOn 
      }; 

Вот рабочий пример: https://dotnetfiddle.net/NZc2Xd

Вот разбивка запроса LINQ:

  1. from book in books является первой таблице вы выбираете из
  2. loans.Where(x => !x.ReturnedOn.HasValue) ограничивает кредиты не более одного предмета за книгу (и кредитов, о которых мы заботимся в этом случае)
  3. join loan in loans on book.BookId equals loan.BookId into result i s присоединяется к таблице кредитов, чтобы мы получили это как часть нашего результирующего набора. Условием присоединения является BookId книги, равнозначно книге BookId
  4. from loanWithDefault in result.DefaultIfEmpty() в основном делает присоединение к кредитам «левым внешним соединением». Это означает, что даже если нет кредитов, которые еще не возвращены для этой книги, вы получите книгу в своем результирующем наборе. В противном случае вы получите только недоступные книги. См. Это Stack Overflow answer за хорошее объяснение разных объединений. Кроме того, см. https://msdn.microsoft.com/en-us/library/bb397895.aspx о том, как делать левые внешние соединения с LINQ.
  5. Наконец, вы выбираете свою модель просмотра из доступного объекта Book и BookLoan.

Вот что подобное сырье SQL-запрос будет выглядеть

select 
    Book.BookId, 
    Book.BookTitle, 
    BookLoan.DueOn 
from 
    Book 
    left outer join BookLoan on 
    Book.BookId = BookLoan.BookId 
    and BookLoan.ReturnedOn is null 
+0

Я получил его и работает, но я не буду лгать, я не совсем понимаю, что происходит в операторе LINQ, и я был интересно, можете ли вы сломать это для меня? –

+1

@ ChristopherJohnson См. Разбивку Я добавил к ответу и дайте мне знать, если это вообще поможет. Запросы LINQ могут быть очень сложными и трудными для чтения. –

+0

работает замечательно. Мне было интересно, что мне нужно сделать, чтобы сделать эту работу для одного запроса? Я надеялся повторно использовать или модифицировать это в представлении деталей, в котором я использовал '.FirstOrDefault()', а также для фильтрации с помощью 'Where whatever.BookId == id'. –