C# linq lambda expression for OR in a list - c#

I have this scenario in which i query with FindByMany (which takes the lambda and returns post if user country and category matches, (as seen in the "else")
But now i need to customize the return with prefered subcategories from users, so what im doing is query n times foreach subcategory and just addRange. I dont want to query 5 times the db if the user has 5 subcategories as favorite, but i dont know how to apply a dinamic OR.
So my question is, how can this code be improved for performance.
var posts = new List<Content>();
if (request.UserId != 0)
{
var user = _userRepository.FindBy(u => u.Id == request.UserId);
if (user != null && user.SubCategories.Any())
{
foreach (var temp in user.SubCategories.Select(subCategory => _contentRepository.FindManyBy(
c =>
c.Country.Id == country.Id && c.Category.Id == theCategory.Id &&
c.SubCategory.Id == subCategory.Id).ToList()))
{
posts.AddRange(temp);
}
}
}
else
{
posts = _contentRepository.FindManyBy(
c => c.Country.Id == country.Id && c.Category.Id == theCategory.Id
).ToList();
}

Could you not just materalise the sub-categories into a list, and then in your FindBy use a thatlist.Contains()?

You can get the user's sub-categories with one query and then use the list and the Contains method to filter the relevant posts. Contains method is supported by most LINQ query provides and should be translated into a single database query.
var subcategories = user.SubCategories.ToList();
foreach (var temp in _contentRepository.FindManyBy(
c =>
c.Country.Id == country.Id && c.Category.Id == theCategory.Id &&
subcategories.Contains( subCategory.Id ) ).ToList()))
{
posts.AddRange(temp);
}

You can build expression for where clause using Expression.OrElse or use enter link description here

The core of the problem is that you're forcing query execution for each item instead of dynamically building the query. #Milney has the right idea; example code below.
IEnumerable<int> subCategoryIds = user.SubCategories.Select(x => x.Id);
var posts = _contentRepository.FindByMany(c => c.Country.Id == country.Id
&& c.Category.Id == theCategory.Id
&& subCategoryIds.Contains(c.SubCategoryId)).ToList();

Related

How to search on the basis of all the variable parameters

I am trying to write a search functionality wherein the user can search on the basis of Id, UserName and status. The data corresponding to these search filters are in different tables, so I have to put joins on these tables.
Of course user can search on the basis of Id and UserName or UserName and status and all the combinations that can be considered from these 3 filters.
What I have done is made different functions to address these combinations.
Is there a way that this can be achieved with one method using linq. I am trying to avoid the if-else and switch.
I have already looked into
Add Conditional Join Dynamically with Linq
and
LINQ - Joins in a dynamic query
This is my code where I am searching on the basis of all the parameters
public List<Application> GetApplication(int? applicationId, string userName, string status)
{
_applicationlist.Clear();
if (applicationId != 0 && !string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(status))
{
var application = (from a in _targetDbContext.DbSet<ApplicationEntity>()
join u in _targetDbContext.DbSet<UserEntity>() on a.InDraftPersonnel equals GetUserID(userName)
join v in _targetDbContext.DbSet<ApplicationVersionEntity>() on a.Id equals v.ApplicationId
join s in _targetDbContext.DbSet<ApplicationStatusEntity>() on v.VersionStatus equals s.Id
where a.Id.Equals(applicationId)
where u.UserName.Equals(userName)
where s.StatusName.Equals(status)
select (new Application
{
Id = a.Id,
AppName = a.AppName,
CreatedBy = a.CreatedBy,
CreatedOn = a.CreatedOn
})).FirstOrDefault();
_applicationlist.Add(application);
}
return _applicationlist;
}
The thing is I am not sure if the user would be searching with all the parameters of with just one.
Any help would be appreciated.
Remove if and try this:
where (!applicationid.HasValue || applicationid == 0 || a.Id.Equals(applicationId)) &&
(string.IsNullOrWhiteSpace(userName) || u.UserName.Equals(userName)) &&
(string.IsNullOrWhiteSpace(status) || s.StatusName.Equals(status))
With this where clause all parameters are optional and if one of those is null or empty those parameter don't affect query result and All combination is supported.
With
...
where a.Id.Equals(applicationId) && u.UserName.Equals(userName) && s.StatusName.Equals(status)
...
... all parameters must match
...
where a.Id.Equals(applicationId) || u.UserName.Equals(userName) || s.StatusName.Equals(status)
...
... at least one parameter must match.
...
where a.Id.Equals(applicationId) && u.UserName.Equals(userName) && s.StatusName.Equals(status)
...
is equivalent to
...
where a.Id.Equals(applicationId)
where u.UserName.Equals(userName)
where s.StatusName.Equals(status)
...
See also: Boolean logical operators (C# reference)
Note that you can construct LINQ queries pice-wise.
var query = source.Join(..) ...;
if (applicationId.HasValue) {
query = query.Where(x => x.Id == applicationId.Value);
}
if (!String.IsNullOrEmpty(userName)) {
query = query.Where(x => x.UserName == userName);
}
if (!String.IsNullOrEmpty(status)) {
query = query.Where(x => x.StatusName == status);
}
query = query.Select(x => ...);

Delete query with Where in LINQ

DELETE From Table
WHERE ID in (1,2,3, ... )
Is there any way to produce following query in LINQ? I tried RemoveRange, but from SQL Server Profiler find that it actually deletes records separately
You could first define the item(s) to remove, then iterate over the list removing them one by one: (note that the whole operation has to be done inside database context scope otherwise it won't work)
var toRemove = list.Where(l => l.id == 1 || l.id == 2 || l.id == 3);
foreach (var item in toRemove)
{
databasecontext.table.Remove(item); //replace databasecontext.table with your own context and table name
}
You can write it in single line
table.RemoveAll(tbl => tbl.id == 1 || tbl.id == 2 || tbl.id == 3);
Hope this helps.
Try this piece of code to make this, It will work for you.
DataContextClass _DbContext = new DataContextClass();
var remove = _DbContext.tableName.where(x=>x.id >= 1 && x.id <= endValue);
if(remove != null)
{
db.logins.RemoveRange(remove);
db.SaveChanges();
}

How to write query in Entity Framework with conditional multiple where condition? [duplicate]

This question already has answers here:
Linq: adding conditions to the where clause conditionally
(9 answers)
Closed 3 years ago.
I am creating a wcf application which is connecting to DB to get some data for customer using Entity Framework. The concept is to search a customer based on the search parameters. User can provide all or few or at least one of the search parameters. But I am quite new in Entity Framework and getting confused on how to do this. I can do this in traditional SQL coding by considering If - Else condition in c# side.
This is my code which is getting the all of the paramters:
var customers = from o in natCustomer.CustomerLists
select o;
customers = customers.Where(c => c.Name == sName && c.Age == iAge
&& c.Gender == sGender && c.Height == dHeight && c.Weight == dWeight
&& c.Nationality == sNationality
&& c.EyeColor == sEyeColor && c.SpecialMark == sSpecialMark);
Please help me by suggesting how do I get the result with few or one parameter only.
Thanks
Entity Framework queries are "deferred" queries. They don't actually run until you start asking for results. This means you can build up a query in pieces and it will (mostly) work exactly like one bigger query.
In your case, you can do something like:
var customers = from o in natCustomer.CustomerLists
select o;
if (!string.isNullOrEmpty(sName))
customers = customers.Where(c => c.Name == sName);
if (!string.isNullOrEmpty(sNationality))
customers = customers.Where(c => c.sNationality == sNationality);
if (!string.isNullOrEmpty(SpecialMark ))
customers = customers.Where(c => c.SpecialMark == SpecialMark);
etc. At the end, when you execute the customers query (for example, call ToList or use a foreach loop) EF will consolidate all of those smaller Where clauses into a single SQL query to run against your data.
Assuming you only want to find customers who match on all non-null parameters, an alternative approach is to include the null checks within the where query, and only compare the parameter to the customer data if the parameter is not null.
customers = customers.Where(c => (string.isNullOrEmpty(sName) || c.Name == sName)
&& (iAge == null || c.Age == iAge)
&& (string.isNullOrEmpty(sGender) || c.Gender == sGender));
You need some way to determine if the given inputs are set or not. For a simplification I assume you receive your parameters as nullables. So you could add an additional condition, if the parameter is provided:
customers = sName == null ? customers : customers.Where(c => c.Name == sName);
customers = iAge == null ? customers : customers.Where(c => c.Age == iAge);
customers = sGender == null ? customers : customers.Where(c => c.Gender == sGender);
...

Build a Linq statement with IsNullOrEmpty check

How can I build a single Linq statement where it check returns all records unless the param is passed? If param is empty then ignore specific 'where'.
I have tried using IsNullOrEmpty within the WHERE but I get an error.
Here are the NON-REQUIRED form field for searching for Invoices.
Invoice Id, Check Number, State Issued
var invoices = ctx.Invoices; <-- get all invoiced
if (inputInvoiceId > 0)
invoices = from i in invoices
where i.id == inputInvoiceId
select i;
if (!string.IsNullOrEmpty(inputCheckNumber))
invoices = from i in invoices
where i.checkNumber == inputCheckNumber
select i;
if (!string.IsNullOrEmpty(inputState))
invoices = from i in invoices
where i.state == inputState
select i;
You could build your query by conditionally appending where clauses like this:
var invoices = ctx.Invoices.AsQueryable();
if (inputInvoiceId > 0)
invoices = invoices.Where(x => x.id == inputInvoiceId);
if (!string.IsNullOrEmpty(inputCheckNumber))
invoices = invoices.Where(x => x.checkNumber == inputCheckNumber);
if (!string.IsNullOrEmpty(inputState))
invoices = invoices.Where(x => x.state == inputState);
return invoices.ToList();
Each additional where clause further filters your results, but the query itself won't be executed (nor any data retrieved) until you call ToList().
What #GrantWinney said will work. Alternatively, you can deal with it in a single query, which may or may not have query compilation/cache benefits if you are concerned about such things:
// Query short-circuit parameters
var invoiceNotSpecified = inputVoiceId == 0;
var checkNumberNotSpecificed = String.IsNullOrEmpty(inputCheckNumber);
var stateNotSpecified = String.IsNullOrEmpty(inputState);
// Query
var invoices = from i in ctx.Invoices
where (invoiceNotSpeficied || i.id == inputInvoiceId) &&
(checkNumberNotSpecified || i.checkNumber == inputCheckNumber) &&
(stateNotSpecified || i.state == inputState)
select i;

Selecting column from LINQ query where cells equals value from a list

I added data to an ObservableCollection from a LINQ query:
foreach (var item in test4)
{
lstPareto.Add(new clsPareto(Convert.ToInt32(item.Step), Convert.ToInt32(item.LogID), test3.Where(p => p.Step.Equals(item.Step) && p.LogID.Equals(item.LogID)).Count()));
}
And this works fine. I get the items I want and convert them to an int when adding them to the list.
Then I have the following queries that pulls data from multiple databases:
int intCmbTestNr = Convert.ToInt32(m_strCmbTestNrSelectedItem);
var productIndex = (from x in m_dcSQL_ConnectionProdTest.DC3_VersionReleases
where x.TestNumber.Equals(intCmbTestNr)
select x.ProductIndex).First();
var version = ((from y in m_dcSQL_ConnectionProdTest.DC3_MainSetups
where y.ProductIndex == productIndex && y.SubVersion == 0
select y.Version).Max());
var versionIndex = (from z in m_dcSQL_ConnectionProdTest.DC3_MainSetups
where z.ProductIndex == productIndex && z.Version.Equals(version) && z.SubVersion == 0
select z.VersionIndex).First();
var subFuncName = from a in m_dcSQL_ConnectionProdTest.DC3_SubFunctions
where a.VersionIndex == versionIndex && a.FunctionNumber == lstPareto.Select(b => b.intStep) && a.SubFunctionNumber == lstPareto.Select(c => c.intStep)
select a.SubFunctionName;
Consider subFuncName. What I am trying to achieve here is to compare a.FunctionNumber to intStep and a.SubFunctionNumber to intLogID of the list lstPareto. However, it says the following: "Operator '==' cannot be applied to operands of type'int' and 'System.Collections.Generic.IEnumerable". I think I know the reason for this, seeing as I'm trying to compare a single int to a whole collection. But how do I compare to every single item intStep and intLogID of the list? I cannot seem to wrap my head around this. Do I use a foreach loop somewhere? Can somebody get me back on track?
Sorry if the title is somewhat vague, couldn't really think of a good one.
If both FunctionNumber and SubFunctionNumber is int:s. Then you can change the condition to this:
lstPareto.Select(b => b.intStep).Contains(a.FunctionNumber)
&& lstPareto.Select(c => c.intStep).Contains(a.SubFunctionNumber)
Update
The reason is most probably because you do not have .ToList(), .First(), .Single() in the query. Depends a little bit what you expect. You could change it to this:
var subFuncName = (from a in m_dcSQL_ConnectionProdTest.DC3_SubFunctions
where a.VersionIndex == versionIndex && lstPareto.Select(b => b.intStep).Contains(a.FunctionNumber)
&& lstPareto.Select(c => c.intStep).Contains(a.SubFunctionNumber)
select a.SubFunctionName).ToList();
Reference:
Enumerable.Contains Method

Categories

Resources