How can i select distinct() based on one field?
I have the following LINQ expression, how can I select distinct by ID field. for some reason i used .Distinct() but keeps shown me a duplicate, I want to show just one record
var customersbyName = await _context.vw_CustomerSearch.AsNoTracking()
.Where(c => c.Name.Contains(request.searchWord))
.Distinct()
.ToListAsync();
try:
var customersbyName = await _context.vw_CustomerSearch.AsNoTracking()
.Where(c => c.Name.Contains(request.searchWord))
.Select(c => c.ID)
.Distinct()
.ToListAsync();
Distinct compare each column of the row, so if one column is different, the row is distinct for linq / sql.
You can try it with a GroupBy:
var customersbyName = await _context.vw_CustomerSearch.AsNoTracking()
.Where(c => c.Name.Contains(request.searchWord))
.GroupBy(i => i.ID)
.Select(x => x.First())
Related
so I have this big table that I want to query and it's so slow, I got a tip that using select is more efficient than include. so how can I convert include to select, in this big query.
ProductEntity? i = await _context.Set<ProductEntity>()
.Include(i => i.Media)
.Include(i => i.Categories)
.Include(i => i.Locations)
.Include(i => i.Reports)
.Include(i => i.Comments)!.ThenInclude(x => x.LikeComments)
.Include(i => i.Bookmarks)
.Include(i => i.Votes)
.Include(i => i.User)!.ThenInclude(x => x.Media)
.Include(i => i.User)!.ThenInclude(x => x.Categories)
.Include(i => i.Forms)!.ThenInclude(x => x.FormField)
.Include(i => i.Teams)!.ThenInclude(x => x.User)!.ThenInclude(x => x.Media)
.Include(i => i.VoteFields)!.ThenInclude(x => x.Votes)
.AsNoTracking()
.FirstOrDefaultAsync(i => i.Id == id && i.DeletedAt == null);
return new GenericResponse<ProductEntity>(_mapper.Map<ProductEntity>(i));
and this is getById method, which just takes one row, I have a getAll that returns a list of products just like this.
The most performant code is always the code that does the least amount of work.
It's possible that what was meant by the 'tip' that "using select is more efficient" may have more to do with answering the question: "do you really, really, really need all that data to be returned?".
Consider, for every .Include you have, EF will be making it's own query to the database to return whatever records are linked by the foreign keys. It's actually pretty efficient on doing that either on the DB side, or in memory if it already has the connected records in memory. (depending on your caching configuration) However, it's going to return the entire Entity referenced. Which may be fine. But, what if each of those 11 sub-entities plus the 7 sub-sub-entities that you have connecting there are each rather large. Do you really, really, really need to return all of that data? Or could you return smaller objects using something like:
var mySmallerObject = await _context.Set<ProductEntity>()
.Include(i => i.Media)
.Include(i => i.Categories)
. . .
.Select(_ => new SomeMuchSmallerObject {
SomeFieldOnProduct = _.SomeFieldOnProduct,
TheOnlyThingINeedFromMedia = _.Media.TheOnlyThingINeedFromMedia,
TheOnlyCategoryFieldINeed = _.Categories.TheOnlyCategoryFieldINeed,
. . . // etc
})
UPDATED
Note: You will still need to either keep all those .Include()s to let EF implicitly build the joins, or else convert them to explicit .Join() statements. By making a smaller object that you use in the .Select() statement, EF will translate this request into a much more optimized query to the DB that only selects the few fields you actually need, and not even return any of the unwanted fields from the database. This also allows you to take full advantage of any indexes you have on the underlying tables in the DB.
Just a thought.
I am trying to replicate this loop as an IQueryable:
foreach (var book in BookList)
{
foreach (var a in book.Authors)
{
if (a.AuthorId.ToString() == Id)
{
AuthorView = new AuthorViewModel(a, item.BookId);
}
}
}
I have tried:
var Test = _context.Book.Include(x => x.Authors)
SelectMany(y => y.Authors).ToList().FirstOrDefault(x => x.AuthorId.ToString() == Id)
.Select(x => new AuthorViewModel(x, ??.BookId);
But I get stuck trying to create the object AuthorViewModel as it requires the BookId that I can no longer access. Is there a way of passing the book Id down?
Sorry, just release the example doesn't make sense.
Books is a table in a database.
Authors is a table in the database which contains many authors.
Book:
BookId
Authors
Authors:
AuthorId
Name
DOB
AuthorList should have been BookList (List). I have corrected this.
I am trying to locate an author based on an author id - and return it in an object (Author, BookId (from the book table))
This is I would do:
var id= int.Parse(Id);
var Test = _context.Book.Include(x => x.Authors)
.SelectMany(item => item.Authors
.Where(y => y.AuthorId == id)
.Select(b => new AuthorViewModel(b,item.BookId));
You can filter and project inside of the SelectMany extension method
I think you don't need SelectMany or Include at all. You can go like;
_context.Book
.Where(b => b.Authors.Any(a => a.AuthorId.ToString() == Id))
.Select(b => new AuthorViewModel(b.Authors.FirstOrDefault(x => x.AuthorId.ToString() == Id), b.BookId);
If you drill down on the Authors (SelectMany) then you have no way to go back to books. The condition on AuthorId needs to be handled internally.
var Test = _context.Book.Include(x => x.Authors)
.FirstOrDefault(x => x.Authors.Any(y => y.AuthorId.ToString() == Id))
.Select(x => new AuthorviewModel(x,x.BookId);
of course this will select only one book per author, just like your code. I wonder if your desired behaviour is to get all books instead...
First of all, convert Id to a number instead of trying to convert AuthorId to a string. Applying any kind of function on a table field means that indexes that cover it can't be used. Instead of a fast search based on an ID, you'll have a full table scan that converts values to strings before comparing them using string semantics. If the Id parameter contains any leading zeros, the comparison will fail.
As for the LINQ query itself, the easiest way is to use the query form. If you want to return one record for each book and author combination, you can write :
var id=int.Parse("ID");
var query= from book in _context.Book
from author in book.Authors
where author.AuthorID = id
select new AuthorViewModel(author,book.BookId);
var records=query.ToList();
All,
I have a query as such:
_AddOrderBy(sortOptions, query)
.Fetch(x => x.ImageType).Eager
.Fetch(x => x.User).Eager
.Fetch(x => x.Partner).Eager
.Inner.JoinAlias(x => x.Partner, () => p).Fetch(x => x.Company).Eager
.Skip(startIndex)
.Take(pageSize)
.List<ImageRequest>();
In the above QueryOver I call _AddOrderBy() method which adds an order by clause. The challenge I face is how do I create an "order by" that references a property (ordering by "CompanyName") that lies within the following association path without conflicting with my Fetch()/Inner joins:
ImageRequest.Partner.Company.CompanyName
Inside my _AddOrderBy() I have this:
Partner p = null;
Company comp = null;
order = query.Inner.JoinAlias(x => x.Partner, () => p)
.Inner.JoinAlias(x => x.Company, () => comp)
.OrderBy(x => comp.CompanyName);
But this gives me a run time exception stating that I have duplicate key (referring to Partner) in the criteria. I can see that this is conflicting with my eager fetching.
My questions is:
How do I add an "order by" so it works with my Fetching.
The beauty of using an Alias in QueryOver is that you don't have to use Fetch or JoinAlias in your _AddOrderBy() method again if the Join happens in the query already. You only need to declare the Alias with the same name.
Therefore your _AddOrderBy() can just look like this:
Partner p = null;
Company comp = null;
order = query
.Inner.JoinAlias(x => p.Company, () => comp) // you can use p here if it was used in the query before
.OrderBy(x => comp.CompanyName);
The reason this works is this: If you put the whole code into one method it will obviously work. Splitting it into two methods still works, because Partner p is not a reference to an object in memory but an Alias that is simply translated into a string for the SQL query.
I have the following code:
List<JobPortalInfo> jobPortalInfos = uow.JobPortalInfoRepository.GetQuery()
.Where(x => x.Job.Id == emailHash.JobId)
.ToList();
var temp = uow.EmailRepository.GetQuery()
.Where(x => jobPortalInfos.Any(y => (y.Contact != null && y.Contact.Id == x.ContactId)))
.ToList();
When I run the 2nd statement, I'm getting the error:
Unable to create a constant value of type 'CR.Data.JobPortalInfo'.
Only primitive types or enumeration types are supported in this context.
JobPortalInfo has a 1-[0 or 1] with Customer and Customer has a 1-* with Email.
Any ideas what I'm doing wrong here?
I figured it out.
jobPortalInfos is Linq to Objects, but EmailRepository is still an IQueryable/Linq To Entites. It doesn't know how to convert the LinqToObject jobPortalInfos to a Sql Server object.
I ended up doing this:
List<JobPortalInfo> jobPortalInfos = uow.JobPortalInfoRepository.GetQuery()
.Where(x => x.Job.Id == emailHash.JobId)
.ToList();
List<long> contactIds = jobPortalInfos
.Where(x => x.Contact != null)
.Select(y => y.Contact.Id)
.ToList();
var temp = uow.EmailRepository.GetQuery()
.Where(x => contactIds.Contains(x.ContactId))
.ToList();
Not sure if there is a more concise way to do this or not, but it seems to work.
I need an example of linq to xml query.
I have two ListBoxes with SelectionMode set to Multiple.
My query for populating first ListBox is:
var query = doc.Root.Descendants("Articles")
.OrderBy(b => b.Element("Category").Value)
.Select(b => b.Element("Category").Value)
.Distinct();
and binding it with:
lbxItems.DataSource = query;
lbxItems.DataBind();
So i have all the values in first ListBox, and when i select item from that ListBox i want to populate second ListBox.
So on SelectedIndexChanged i have another query:
var query = doc.Root.Descendants("Articles")
.Where(b => b.Element("Category").Value.Equals(lbxItems.SelectedValue))
.OrderBy(b => b.Element("SubCategory").Value)
.Select(b => b.Element("SubCategory").Value)
.Distinct();
That's working if i select one item, but i need a query that is doing the same thing but from multiple selected items.
Thank you.
Try changing your where clause like so:
.Where(b => lbxItems.Items
.Cast<ListItem>() // needs a cast
.Where(i => i.Selected)
.Select(i => i.Value)
.Contains(b.Element("Category").Value))
The idea is to determine what items are selected and see if your category value is among those selected.