Building a custom|progressive query in LINQ? - c#

I have a page with five text boxes, each one representing a field in my database table and a search button:
If I were using SQL I could build my SQL statement depending on which fields have data in them.
However, I want to use LINQ, and I'm at a loss as to how to accomplish this. For instance, take a look at the query below:
var db = new BookDBDataContext();
var q =
from a in db.Books
where a.Title.Contains(txtBookTitle) &&
a.Author.Contains(txtAuthor) &&
a.Publisher.Contains(txtPublisher)
select a.ID;
The query above will return data where all the fields match data in the table. But, what if the user didn't enter an Author in the txtAuthor field? If I were building this as a query string, I could check each field for data and add it to the query string. Since this is LINQ, I can't dynamically change the search criteria, it seems.
Any advice would be greatly appreciated!

var db = new BookDBDataContext();
var q = (from a in db.Books
where a.Title.Contains(txtBookTitle));
if(!String.IsNullOrEmpty(txtAuthor))
{
q = q.Where(a => a.Author.Contains(txtAuthor));
}
if(!String.IsNullOrEmpty(txtAuthor))
{
q = q.Where(a => a.Publisher.Contains(txtPublisher));
}
var id = q.Select(a => a.ID);

from a in db.Books
where (string.isNullorWhiteSpace(search) || a.Title.Contains(search)) &&
(string.isNullorWhiteSpace(txtAuthor) || a.Author.Contains(txtAuthor) ) &&
(string.isNullorWhiteSpace(txtPublisher) || a.Publisher.Contains(txtPublisher))
select a.ID;

Related

C# LINQ Filter records in child tables

I have a main table "SALES" and two secondary tables "PRODUCTS" and "SERVICES", I need to select only the records in "SALES" that contain some product or service entered by the user, I don't need to bring the sales records and products, just filter. First I made the filter in the table "SALES" by date of sale:
var query = (from p in _contexto.sales
where p.datesale.Value.Date >= Convert.ToDateTime(strDtI).Date &&
p.datesale.Value.Date <= Convert.ToDateTime(strDtF).Date
select p);
Now let's say the user wants to filter also the sales that have products or services with the words in a string Array
words = ['apple', 'beef', 'cleaning', 'haircut']
if you receive the array of words, I tried the filter below, but it didn't work, it kept bringing all the records.
var queryi = (from i in _contexto.products
where words.Contains(i.name) || words.Contains(i.description) select i);
//var queryj = (from i in _contexto.services
//where words.Contains(i.name) || words.Contains(i.description) select i);
//query = query.Where(p => queryi.All(c => c.idsale != p.id) || queryj.All(c => c.idsale != p.id));
query = query.Where(p => queryi.All(c => c.idsale != p.id));
where am I failing, and is there a better and more performant way to do this?
Thank you!
Using more descriptive variable names, and assuming you meant to only find products that have the exact same name or description as one of the words, you would have:
var salesInPeriod = from s in _contexto.sales
where Convert.ToDateTime(strDtI).Date <= s.datesale.Value.Date &&
s.datesale.Value.Date <= Convert.ToDateTime(strDtF).Date
select s;
var matchingidsales = from p in _contexto.products
where words.Contains(p.name) || words.Contains(p.description)
select p.idsale;
var ans = from s in salesInPeriod
where matchingidsales.Contains(s.id)
select s;
PS: I inverted the date comparison since I think it makes it easier to see you are doing a between test.

Linq: let Count result into a specified column

I've got a table Installation which can contains one or many Equipements.
And for functionnal reasons, I've overwritten my table Installation and added a field NbrEquipements.
I want to fill this field with Linq, but I'm stuck...
Due to special reasons, there is no relation between these to tables. So, no Installation.Equipements member into my class. Therefore, no Installation.Equipements.Count...
I'm trying some stuff. Here is my code:
var query = RepoInstallation.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
int?[] etatIds = { 2, 3 };
query = (from i in query
select new Installation
{
NbrEquipements= (from e in RepoEquipement.AsQueryable()
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
});
But with this try, I got this error:
The entity or complex type 'myModel.Installation' cannot be constructed in a LINQ to Entities query
I've tried some other stuff but I'm always turning around...
Another thing that can be useful for me: It would be great to fill a field called Equipements which is a List<Equipement>.
After that, I would be able to Count this list...
Is it possible ?
Tell me if I'm not clear.
Thanks in advance.
Here is the final code:
//In the class:
[Dependency]
public MyEntities MyEntities { get; set; }
//My Methode code:
var query = MyEntities .SasInstallations.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
var liste = new List<Installation>();
var queryWithListEquipements =
from i in query
select new
{
Ins = i,
EquipementsTemp = (from eq in MyEntities.Equipements.AsQueryable()
where eq.SpecialId == i.SpecialId
&& (etatIds.Contains(eq.SasEquEtat))
select eq
).ToList()
};
var listWithListEquipements = queryWithListEquipements.ToList();
foreach (var anonymousItem in listWithListEquipements)
{
var ins = anonymousItem.Ins;
ins.Equipements = anonymousItem.EquipementsTemp;
ins.NumberEquipements = ins.Equipements.Count();
liste.Add(ins);
}
return liste;
By the way, this is very very fast (even the listing of Equipements). So this is working exactly has I wished. Thanks again for your help everyone!
Use an anonymous type. EF does not like to instantiate entity classes inside a query.
var results = (from i in query
select new
{
NbrEquipements= (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
})
.ToList();
Notice how I used select new instead of select new Installation.
You can then use the data inside the list (which is now in memory) to create instances of type Installation if you want like this:
var installations = results.Select(x =>
new Installation
{
NbrEquipements = x.NbrEquipements
}).ToList();
Here is how to obtain the list of equipment for each installation entity:
var results = (from i in query
select new
{
Installation = i,
Equipment = (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e).ToList()
})
.ToList();
This will return a list of anonymous objects. Each object will contain a property called Installation and another property called Equipment (which is a list). You can easily convert this list (of anonymous objects) to another list of whatever type that you want.

C# Linq Select Rows Where ID Equals ID in CSV

What I have is a string of comma separated IDs that I'm receiving from a query string (e.g. 23,51,6,87,29). Alternately, that string could just say "all".
In my Linq query I need a way to say (in pseudo code):
from l in List<>
where l.Id = all_of_the_ids_in_csv
&& other conditions
select new {...}
I'm just not sure how to go about doing that. I'm not even sure what to google to get me going in the right direction. Any pointing in the right direction would be extremely helpful.
I would suggest to split your query in 2 - first part will select by ID, and the select one will select other conditions.
First of all: check if query string contains numbers, or is just all:
var IEnumerable<ListItemType> query = sourceList;
if(queryStringValue != "All")
{
var ids = queryStringValue.Split(new[] { ',' })
.Select(x => int.Parse(x)) // remove that line id item.Id is a string
.ToArray();
query = query.Where(item => ids.Contains(item.Id));
}
from l in query
// other conditions
select new {...}
Because LINQ queries have deffered execution you can build queries like that without performance drawback. Query won't be executed until you ask for results (by ToList call or enumeration).
If you really want it with just one LINQ query:
var idArray = all_of_the_ids_in_csv.Split(',');
from l in List<>
where (all_of_the_ids_in_csv == "All" || idArray.Contains(l.Id))
&& other conditions
select new {...}
The trick is using string.Split
var ids = string.split(rawIdString, ",").ToList();
var objects = ids.Where(id=> /*filter id here */).Select(id=>new { /* id will be the single id from the csv */ });
// at this point objects will be an IEnumerable<T> where T is whatever type you created in the new statement above

How to use sql IN statement in linq lambda with 2 statements

My statement is
Contents.
Select(x=> new
{
ContentUsers = x.ContentUsers.
Where(t=>t.UserId==2).
Select(t=>t.ContentId)
}).
Where(y=>y.ContentUsers.Any())
It gives me some Ids that i want to use it my another statements.
Contents.Where(x=>x.Id == 633,634,635)
How can i merge them?
What you're missing is the Contains function. It will probably be simpler to express this in a single query expression. If I translate your code to a single query expression, I looks a something like this:
var content =
from c in Contents
let contentUsers =
from x in Contents
select new
{
ContentUsers =
from t in x.ContentUsers
where t.UserId == 2
select t.ContentId
}
where contentUsers.Any(cu => cu.ContentUsers.Contains(c.Id))
select c;
However, it looks like all you want is to get just the Content records associated with a given UserID in the ContentUsers collection. This is a lot easier.
var content =
from c in Contents
where c.ContentUsers.Any(t => t.UserId == 2)
select c;
or if you prefer
var content = Contents.Where(c => c.ContentUsers.Any(t => t.UserId == 2));

filter a linq query based on the results of another query's results

I am wanting to filter a linq query
I have 2 linq statements
The 1st gets all the stores I want and the 2nd is where I filter information based on the results found in the 1st query.
var stores = ctx.Stores.Where(ps => ps.ParentStoreID == parent.ParentStoreID && ps.StoreID!=storeID);
var query = (from a in ctx.TransactionTable
from b in ctx.MappingTable.Where(x => x.TransactionId== a.TransactionId).DefaultIfEmpty()
where a.StoreID!=storeID
select new
{
Transactions = a,
Mapping = b
}).ToList();
How do I add another where clause into my 2nd query to only return results where a.StoreId is contained within the stores result?
Like this:
var stores = ctx.Stores.Where(ps => ps.ParentStoreID == parent.ParentStoreID && ps.StoreID!=storeID);
var query = (from a in ctx.TransactionTable
from b in ctx.MappingTable.Where(x => x.TransactionId==a.TransactionId).DefaultIfEmpty()
where a.StoreID!=storeID && stores.Select(s => s.StoreID).Contains(a.StoreID)
select new
{
Transactions = a,
Mapping = b
}).ToList();
You can find more info here:
Linq to Entities - SQL "IN" clause

Categories

Resources