LINQ Contains() alternatives [duplicate] - c#

This question already has answers here:
Scalable Contains method for LINQ against a SQL backend
(5 answers)
Closed 3 years ago.
I have a list of int and I need to select all record from my query where the id is contained in the second list as showed below:
//my list of ids
var ids=[myquery].select(x=> x.id)
query = query.Where(x => ids.Contains(x.Id));
Now LINQ will convert the above in :
SELECT *
FROM [MyTable]
WHERE ([x].[id] IN (108,687, 689, 691, 694, 705, 703,.....)
Now the ids list will grow a lot and I guess this will ruin the performances.
What would be a better solution considering the the ids list will contain more than 200K item ?

It depends on your model, but you should probably use a navigation property.
Given that you currently have something like this:
var ids =
context
.Entity1
.Where(x => x.Property == value)
.Select(x => x.ID)
.ToHashSet();
var items =
context
.Entity2
.Where(x => ids.Contains(x.ID))
.ToList();
write something like this instead:
var items =
context
.Entity2
.Where(x => x.Entity1.Property == value)
.ToList();
You may need to add such an association to your model before this will be possible.

I would suggest to create a stored procedure as:
there is no unnecessary calls between C# and database(e.g., you are collecting these 200K ids)
less code in C#. So your code will be cleaner and clearer
performance is better as sometimes EF generates inefficient SQL code
So calling stored procedure would look like this:
var user = "johndoe";
var blogs = context.Blogs
.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser {0}", user)
.ToList();
OR try to use NOT IN operator if items are less 200K:
query = query.Where(x => !ids.Contains(x.Id));
SQL:
SELECT *
FROM [MyTable]
WHERE ([x].[id] NOT IN (108,687, 689, 691, 694, 705, 703,.....)

Related

How to combine a query of the LINQ and a list? [duplicate]

This question already has answers here:
Linq version of SQL "IN" statement
(6 answers)
Closed 4 years ago.
[Sorry if it is a duplicate]
I couldn't find properly solution, so I decided to ask a question.
I have an object companies which returns list of elements.
I would like to write a query which will select these all CompanyId which we have in our list. I don't want to select only one record by using FirstOrDefault().
Example:
var companiesSummary = _context.Company
.Where(c => c.CompanyId == companies.Select(cs => cs.ID))
.Include(country => country.Country)
How can I cope with it? Do you have any ideas?
Select the ids of the companies from your in-memory list and then pass that into the query in the where method:
var ids = companies.Select(cs => cs.ID).ToList();
var companiesSummary =
_context.Company
.Where(c => ids.contains(c.ID))
.Include(country => country.Country)
Assuming your companies contains a list of objects with an ID property you want to compare to Company.CompanyId, your query should look like
int[] ids = companies.Select(cs => cs.ID).ToArray();
var companiesSummary = _context.Company
.Where(c => ids.Contains(c.CompanyId))
.Include(company => company.Country);
var matchingCompanies = companies.Where(c => companyIds.Contains(c.Id))
Make companyIds a HashSet<T> for an efficient Contains.

LINQ Query find columns where string contains any letter

I'm trying to find all customer codes where the customer has a status of "A" and whose code does not contain any letter using LINQ query.
var activeCustomers = Customers.Where(x => x.Status == "A" && x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
When I run this query in LinqPad I get the following error:
You'll need to do this as a two part query. First, you could get all the users who's status is "A":
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
After you've got those in-memory, you can create an additional filter for char.IsDigit:
var codes = activeCustomers.Where(x => x.Code.Any(n => !char.IsLetter(n)))
.Select(x => x.Code)
.ToArray();
As commenters have stated, IsLetter() cannot be translated to SQL. However, you could do the following, which will first retrieve all items with Status "A" from the database, then will apply your criteria after retrieval:
var activeCustomers = Customers.Where(x => x.Status == "A").AsEnumerable().Where(x => x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
You'll have to determine if it's acceptable (from a performance perspective) to retrieve all customers with "A" and then process.
The AsEnumerable() transitions your LINQ query to working not with IQueryable (which works with SQL) but with IEnumerable, which is used for plain LINQ to objects.
Since it is LINQ 2 SQL, there is no natural way to translate char.IsLetter to something SQL can understand. You can hydrate a query that retrieves your potential candidates and then apply an addition in-memory filter. This also solves the issue where LINQ 2 SQL has a preference for a string and you are dealing with chars
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
var filteredCustomers = activeCustomers.Where(x =>
x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code).ToList();
There are two performance hits here. First, you're retrieving all potential records, which isn't too desirable. Second, in your above code you were only interested in an enumerable collection of codes, which means our query is including far more data than we originally wanted.
You could tighten up the query by only returning back to columns necessary to apply your filtering:
var activeCustomers = Customers.Where(x => x.Status == "A")
Select(x => new Customer{ Status = x.Status, Code = x.Code }).ToList();
You still return more sets than you need, but your query includes fewer columns.

Linq to SQL order by with Distinct

My Environment: ASP.net and C# in VS 2013 Express.
I have been through many similar SO articles trying to work this out. I am amateur with Linq to SQL queries and c# in general.
I'm trying to use Linq to SQL to get the top 5 most recent distinct values from a column, then add them to a list. My application is asp.net using c# and a .dbml file for data abstraction.
I've tried it many different ways. I either get non-distinct yet sorted list, or I get a distinct unsorted list. What I have so far is below
var Top5MFG = (from mfg in db.orders
where mfg.manufacturer.Length > 0 && mfg.customerid == "blahblahblahblahblah"<br />
select new {
manufacturer = mfg.manufacturer,
date = mfg.date_created
})
.Distinct()
.OrderByDescending(s => s.date);
I'm thinking my "Distinct" is looking at the "ID" column, and perhaps I need to tell it I want it to look at the "manufacturer" column, but I haven't worked out how / if it's possible to do that.
I could do this with ease by using a storedproc, but I'm really trying to do it with c# code directly if possible. This is my first post to SO, I hope I have put it together properly. Any help much appreciated.
Thanks
No the Distinct compares manufacturer and date pairs.If you want to get distinct records by manufacturer then I recommend DistinctBy method.It's in the MoreLINQ library.Since its a third library method it's not supported in linq to sql, you still can use it by fetching the records from DB and do the rest in memory
(from mfg in db.orders
where mfg.manufacturer.Length > 0 && mfg.customerid == "blahblahblahblahblah"
select new {
manufacturer = mfg.manufacturer,
date = mfg.date_created
})
.AsEnumerable()
.DistinctBy(x => x.manufacturer)
.OrderByDescending(s => s.date)
.Take(5);
I think you can use the GroupBy to do what you want.
var Top5MFG = db.orders
.Where (x => x.manufacturer.Length > 0 && x.customerid == "blahblahblahblahblah")
.GroupBy(mfg => mfg.manufacturer)
.Select(g => g.First())
.OrderByDescending(d => d.date_created );
.Take(5);
One way you can distinct by a certain field is to replace:
...
.Distinct()
...
with:
...
.GroupBy(x => x.manufacturer )
.Select(g => g.First())
...

Combine two condition from two table with OR clause in orchard HQL query

In orchard cms i want to write a query containing two condition over two different ContentPartRecord combined with OR clause. one possible way to combine conditions is :
var query = _contentManager.HqlQuery()
.Where(a => a.ContentPartRecord<ProductPartRecord>(), a => a.Eq("Title", "myTitle"))
.Where(a => a.ContentPartRecord<TitlePartRecord>(), a => a.Eq("Price", 1000))
.List();
but this one combines two condition with And clause. i think something like following along with some changes (which would be the answer for this question) could be the case:
var query = _contentManager.HqlQuery()
.Join(a => a.ContentPartRecord<ProductPartRecord>())
.Join(a => a.ContentPartRecord<TitlePartRecord>())
.Where(a => a.ContentItem(),
a => a.Or(p => p.Eq("ProductPartRecord.Price",
"1000"), t => t.Eq("TitlePartRecord.Title", "myTitle")))
.List();
but i couldn't get it working.any body have any suggestion?
When you use where clause in HqlQuery you need to pass Alias (as first parameter). Alias means that you will be apply where clause to specific table (class that represent this table). And when you use OR clause that you definitely need to compare columns of the specific table in two parts of OR clause (left and right from the OR clause). And i think this is not possible to do you need a standard way.
But you can use:
Action<IAliasFactory> productPartRecordAlias = x => x.ContentPartRecord<ProductPartRecord>().Named("productPartRecord");
Action<IAliasFactory> titlePartRecordAlias = x => x.ContentPartRecord<TitlePartRecord>().Named("titlePartRecord");
var query = _contentManager.HqlQuery()
.Join(productPartRecordAlias)
.Join(titlePartRecordAlias)
.Where(a => a.ContentItem(), p => p.Gt("Id", "0 AND (productPartRecord.Price = 1000 OR titlePartRecord.Title = 'myTitle')"));

LINQ - Preserve order across objects with .Any()? [duplicate]

This question already has answers here:
List sort based on another list
(3 answers)
Closed 9 years ago.
I am building a search function which needs to return a list ordered by relevance.
IList<ProjectDTO> projects = new List<ProjectDTO>();
projects = GetSomeProjects();
List<ProjectDTO> rawSearchResults = new List<ProjectDTO>();
//<snip> - do the various search functions here and write to the rawSearchResults
//now take the raw list of projects and group them into project number and
//number of search returns.
//we will sort by number of search returns and then last updated date
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.Select(x => new
{
Count = x.Count(),
ProjectNbr = x.Key,
LastUpdated = x.First().UpdatedDateTime
})
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.LastUpdated);
So far so good; the "orderedProjects" variable returns my list in the correct order. However, I need the entire object for the next step. When I try to query back to get the original object type, my results lose their order. In retrospect, this makes sense, but I need to find a way around it.
projects = (from p in projects
where orderedProjects.Any(o => o.ProjectNbr == p.ProjectNbr)
select p).ToList();
Is there a LINQ-friendly method for preserving the order in the above projects query?
I can loop through the orderedProject list and get each item, but that's not very efficient. I can also rebuild the entire object in the original orderedProjects query, but I'd like to avoid that if possible.
You need to do it the other way around:
Query orderedProjects and select the corresponding items from projects:
var projects =
orderedProjects
.Select(o => projects.SingleOrDefault(p => p.ProjectNbr == o.ProjectNbr))
.Where(x => x != null) // This is only necessary if there can be
// ProjectNbrs in orderedProjects that are not in
// projects
.ToList();
You shouldn't use "Select" in the middle there as that operator transforms the object into another type and you say that you need the original object.
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.First().UpdatedDateTime);
Do they come in chronological order or something? Otherwise, I'm pretty sure you want the "ThenByDescending" to be performed on the newest or oldest project update like so:
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.Max(p=>p.UpdatedDateTime));

Categories