Hello I am trying to convert the following SQL statement into its LINQ equivalent and since I am really new to .net (coding for one day) i have gotten stuck on this for hours now.
SELECT *
FROM Books
WHERE BookID IN (SELECT BookID
FROM Borrows
WHERE UserID = 2)
This is the model
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; }
}
I would greatly appreciate anyones help.
EDIT
Context class
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; }
}
Assuming your context is called db, you could do the following query
var borrowedBooksForUser = db.Books
.Where(b => db.Borrowed.Any(x => x.UserID == 2 && x.BookID == b.BookID));
It might be preferable to do this with a join.
The argument is: If a user borrows huge amounts of books, or there is an error in the data, then your subquery could return a lot of IDs, and SQL 'IN' clauses on long lists can get really slow.
Using a join:
SQL query:
SELECT Books.* FROM Books
JOIN Borrows ON Borrows.BookID = Books.BookID
WHERE Borrows.UserID = 2
Linq statement:
var allBooksBorrowedByUser2 = db.Borrowed
.Where(borrow => borrow.UserID == 2)
.Join(db.Books,
borrow => borrow.BookID,
book => book.BookID,
(borrow, book) => book);
Navigation would make everything more simple.
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);
You Could Do something Like This:
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);
try this,
var getResult=from b in db.Books
join bo in db.Borrows on b.BookID=bo.BookID
where bo.UserID=2
Related
My scenario: Users will be able to create lists and add items to these lists. What I want to do is to find the items in the lists created by the users at most.
Item Entity
public class Item:BaseEntity
{
public string Name { get; set; }
public decimal Price { get; set; }
public decimal DiscountedPrice{ get; set; }
public virtual ICollection<ItemList> ItemLists { get; set; }
}
Item List Entity
public class ItemList:BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public int UserId { get; set; }
public ICollection<Item> Items { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
User Entity
public class User:BaseEntity
{
public string Name { get; set; }
public string Surname { get; set; }
public string Gsm { get; set; }
public string Email { get; set; }
public virtual ICollection<ItemList> ItemLists{ get; set; }
}
my DTO
public class TopItemsForUsers
{
[BsonRepresentation(BsonType.ObjectId)]
[BsonId]
public string ItemId { get; set; }
public string UserId { get; set; }
public int Quantity { get; set; }
}
My Item repository
var query = _context.Items.Include(l => l.ItemLists)
.GroupBy(g => g.ItemLists)
.Select(z => new TopItemsInLists { ItemId = z.Key.ToString(), Quantity = z.Count() })
.OrderByDescending(z => z.Quantity)
.Take(10);
I want to get products that are very present in users' lists
Where am I doing wrong? If anyone has any other suggestions
Try this query. I hope I understand question correctly.
var query =
from u in _context.Users
from il in u.ItemLists
from i in il.Items
group i by new { UserId = u.Id, ItemId = i.Id } into g
select new TopItemsInLists
{
UserId = g.Key.UserId.ToString(),
ItemId = g.Key.ItemId.ToString(),
Quantity = g.Count()
};
query = query
.OrderByDescending(z => z.Quantity)
.Take(10);
I have 3 model classes as follows:
public class Bill
{
[Key]
public int BillId { get; set; }
public string BillNumber { get; set; }
public string BillStatus { get; set; }
public int Amount { get; set; }
}
public class FuelExpense
{
[Key]
public int FuelExpenseId {get; set; }
public string Vehicle { get; set; }
public FuelTypesEnum FuelType { get; set; }
[ForeignKey("Bill")]
public int BillId { get; set; }
public virtual Bill Bill { get; set; }
}
public class BooksAndNotesExpense
{
[Key]
public int BooksAndNotesExpenseId { get; set; }
public string Publication { get; set; }
[ForeignKey("Bill")]
public int BillId { get; set; }
public virtual Bill Bill { get; set; }
}
I want to fetch all pending bills with their respective expense id. For example my tables look like below:
I want a result like this:
How to get it in Entity Framework in ASP.NET MVC using C# ? If there is a better way to re-structure my models, please let me know too. Thanks in advance!
I do not know much about Entity FrameWork But it looks like basic linq can do
var data =
dbContext.FuelExpense.Where(x => x.Bill.BillStatus == 'Pending')
.Select(x => new Data(x.BillId, x.Bill.BillNumber, x.Bill.BillStatus, x.Bill.Amount, x.FuelExpenseId))
.Concat(
dbContext.BooksAndNotesExpense.Where(x => x.Bill.BillStatus == 'Pending')
.Select(x => new Data(x.BillId, x.Bill.BillNumber, x.Bill.BillStatus, x.Bill.Amount, x.BooksAndNotesExpenseId))
)
.ToList()
OR
var data =
dbContext.BooksAndNotesExpense
.Join(
dbContext.Bill,
booksExpense => booksExpense.BillId,
bill => bill.BillId,
(booksExpense , bill) => new Data(bill.BillId, bill.BillNumber, bill.BillStatus, bill.Amount, booksExpense.FuelExpenseId)
)
.Where(x => BillStatus == 'Pending')
.Concat(
dbContext.FuelExpense
.Join(
dbContext.Bill,
fuelExpense => fuelExpense.BillId,
bill => bill.BillId,
(fuelExpense, bill) => new Data(bill.BillId, bill.BillNumber, bill.BillStatus, bill.Amount, fuelExpense.BooksAndNotesExpenseId)
)
.Where(x => BillStatus == 'Pending')
)
.ToList()
public class Data {
public Data(int billId, string billNumber, string billStatus, int amount, int expenseId) {
BillId = billId;
BillNumber = billNumber;
BillStatus = billStatus;
Amount = amount;
ExpenseId = expenseId;
}
public int BillId { get ; set; }
public string BillNumber { get ; set; }
public string BillStatus { get ; set; }
public int Amount { get ; set; }
public int ExpenseId { get ; set; }
}
Is there any way I can avoid using Include and ThenInclude in EF Core ?
I have these models and dtos :
For Book:
public partial class Book
{
public Book()
{
BookAuthors = new HashSet<BookAuthor>();
BookCategories = new HashSet<BookCategory>();
Reviews = new HashSet<Review>();
}
public int BookId { get; set; }
public string Title { get; set; }
...
public string ImageUrl { get; set; }
public ICollection<BookAuthor> BookAuthors { get; set; }
public ICollection<BookCategory> BookCategories { get; set; }
public ICollection<Review> Reviews { get; set; }
}
public class BookDto
{
public int BookId { get; set; }
public string Title { get; set; }
...
public string ImageUrl { get; set; }
public IList<AuthorDto> Authors { get; set; }
public IList<CategoryDto> Categories { get; set; }
public IList<ReviewDto> Reviews { get; set; }
}
For Author :
public partial class Author
{
public Author()
{
BookAuthors = new HashSet<BookAuthor>();
}
public int AuthorId { get; set; }
public string AuthorName { get; set; }
...
public ICollection<BookAuthor> BookAuthors { get; set; }
}
public class AuthorDto
{
public int AuthorId { get; set; }
public string AuthorName { get; set; }
...
public IList<BookDto> Books { get; set; }
}
For Category:
public partial class Category
{
public Category()
{
BookCategories = new HashSet<BookCategory>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public ICollection<BookCategory> BookCategories { get; set; }
}
public class CategoryDto
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public IList<BookDto> Books { get; set; }
}
And Review :
public partial class Review
{
public int ReviewId { get; set; }
public int BookId { get; set; }
public int UserId { get; set; }
public DateTime? Date { get; set; }
public string Comments { get; set; }
public decimal? Rating { get; set; }
public Book Book { get; set; }
public User User { get; set; }
}
public class ReviewDto
{
public int ReviewId { get; set; }
public int BookId { get; set; }
public int UserId { get; set; }
public DateTime? Date { get; set; }
public string Comments { get; set; }
public decimal? Rating { get; set; }
public Book Book { get; set; }
public User User { get; set; }
}
I have this :
public IEnumerable<Book> GetAll()
{
var books = _context.Book
.Include(e => e.BookAuthors)
.ThenInclude(a => a.Author)
.Include(c => c.BookCategories)
.ThenInclude(categ => categ.Category)
.Include(r => r.Reviews)
.AsNoTracking()
.ToList();
return books;
}
And then in Author :
public IEnumerable<Author> GetAll()
{
var authors = _context.Author
.Include(e => e.BookAuthors)
.ThenInclude(b => b.Book)
.ToList();
return authors;
}
public Author GetById(int id)
{
return _context.Author.Include("BookAuthors.Book").SingleOrDefault(x =>
x.AuthorId == id);
}
Between Books and Authors, Books and Categories I have many to many relationship, between Review and Books one to many relationship.
I need this because on the list with books I display the name of the author as well, on an author detail page I display his books and so on. I'm using AutoMapper and DTOs as well.
The same for Categories, Reviews..my json with the returned data becomes very big and it takes a lot of time to load the data into the page, because it has this nested structure. What would be the best solution to do this ?
There's a way to do Eager loading. I tried by GroupJoin(expression).SelectMany(...).
This will allow you to load till one level avoiding circular rerefence. I'll show you how I archived it, but with your models.
You have:
var books = _context.Book
.Include(e => e.BookAuthors)
.ThenInclude(a => a.Author)
.Include(c => c.BookCategories)
.ThenInclude(categ => categ.Category)
.Include(r => r.Reviews)
.AsNoTracking()
.ToList();
return books;
By the way, you dont put BookAuthors model. So, I'll assume it's structure:
var books = _context.Authors
.GroupJoin(_context.Book,
t1 => new { IdAuthor = (Guid)t1.Id }, //where t1 = Authors, you should have IdAuthor in Book.
a => new { IdAuthor = (Guid)a.IdAuthor }, //where a = Book
(t1, a_join) => new { t1, a_join })
.SelectMany(t1 => t1.a_join.DefaultIfEmpty(), (t1, a) => new { t1, a }) //.DefaultIfEmpty() = LEFT JOIN
.Select(x => new AuthorBooksDTO
{
IdAutor = t1.t1.Id //please navegate t1 till VS shoows you which model is
Books = t1.t1.a_join.toList() //navegate t1. a_join will be the list of books.
....
})
.ToList();
For sure, it takes more time to build but performance improve incredibly.
Let us know if it works for you.
I have a legacy class database that is represented by the following model.
public class Course
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public CourseLevel Level { get; set; }
public float FullPrice { get; set; }
public Author Author { get; set; }
public IList<Tag> Tags { get; set; }
public IList<Attendee> Attendees { get; set; }
}
public class Attendee
{
public int Id { get; set; }
public int StudentId { get; set; }
public decimal Tuition { get; set; }
public Student Student { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Course> Courses { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Course> Courses { get; set; }
}
I need to get a list of classes that either a part of the course title or description or a part of a student's name matches my search string. The first part is easy.
List<Course> courses = db.Courses.Where(w => w.Title.IndexOf(searchString) > -1 || w.Description.IndexOf(searchString) > -1).ToList();
How do I now filter against w.Attendees.Student.Name?
I tried:
List<Course> courses = db.Courses
.Where(w => w.Title.IndexOf(searchString) > -1 ||
w.Description.IndexOf(searchString) > -1 ||
w.Attendees.Any(a => a.Student.Name.IndexOf(searchString) > -1)).ToList();
And it just returns an empty list.
I'm still kind of new to Linq, I'm coming from Grails. Any help is appreciated.
Try running only w.Attendees.Any(a => a.Student.Name.IndexOf(searchString) and debugging it because Attendees could be null or empty, and the same is true for the Student property.
Also, in the off chance that your database isn't case insensitive, you should consider changing your code to reflect that:
w.Attendees.Any(a => a.Student.Name.ToLowerInvariant().Contains(searchString.ToLowerInvariant())
The case sensitiveness could be the source of your problems too.
Try this:
List<Course> courses = db.Courses
.Where(w => w.Title.Contains(searchString)||
w.Description.Contains(searchString) ||
w.Attendees.Any(a => a.Student.Name.Contains(searchString))).ToList();
I'm building a system for producing surveys and handling the responses, I have a viewmodel SurveyTakeViewModel
namespace Survey.ViewModel
{
public class SurveyTakeViewModel
{
public Models.Survey Survey { get; set; }
public bool TakenBefore { get; set; }
}
}
namespace Survey.Models
{
public class Survey
{
[Key]
public int SurveyId { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public string MessageStart { get; set; }
public string MessageEnd { get; set; }
public virtual ICollection<Question> Question { get; set; }
}
}
namespace Survey.Models
{
public class Question
{
public virtual int SurveyId { get; set; }
[Key]
public int QuestionId { get; set; }
public int DisplayOrder { get; set; }
public string QuestionText { get; set; }
public string QuestionNote { get; set; }
public int QuestionTypeId { get; set; }
public virtual QuestionType QuestionType { get; set; }
public virtual ICollection<QuestionAnswerOption> QuestionAnswerOption{ get; set; }
public virtual ICollection<QuestionAnswer> QuestionAnswer { get; set; }
}
}
namespace Survey.Models
{
public class QuestionAnswer
{
public virtual int QuestionId { get; set; }
[Key]
public int QuestionAnswerId { get; set; }
public int SurveyId { get; set; }
public string UserId { get; set; }
public string QuestionActualResponse { get; set; }
}
}
It all works fine for the questionnaire, however when a user revisits a questionnaire they have previously answered I want to populate the QuestionAnswer part of the viewmodel with answers only by a specific userid, at the moment I get everyanswer. I have tried loads of different Linq queries, initially my ICollections<> were List<>, which I am told could cause all records to be returned.
at the moment I am using
Survey = db.Survey.FirstOrDefault(s => s.SurveyId == 1)
which returns all QuestionAnswer
I have tried things like
Survey = db.Survey.FirstOrDefault(a => a.Question
.Any(b => b.QuestionAnswer
.Any(c => c.UserId == userId)));
but it still returns all QuestionAnswer for every userId
It seems like you know which survey you want, but when you access the various properties, they are populating with extra information... you'd like fewer grandchild records.
I don't know enough LinqToEntities to limit the loading in the way that is needed (LinqToSql would use DataLoadOptions.LoadsWith and DataLoadOptions.AssociateWith). Instead, I offer this manual shaping of the data - after loading. Perhaps it will help an expert understand the question and then they can express it in a LinqToEntities query.
int surveyId = GetSurveyId();
int userId = GetUserId();
Survey mySurvey = db.Survey.FirstOrDefault(s => s.SurveyId == surveyId);
ILookup<int, QuestionAnswer> qaLookup = db.QuestionAnswer
.Where(qa => qa.SurveyId == surveyId && qa.UserId == userId)
.ToLookup(qa => qa.QuestionId);
foreach(Question q in mySurvey.Questions)
{
//limit q.QuestionAnswer to only this user's answers.
q.QuestionAnswer = qaLookup[q.QuestionId].ToList();
}