How to LINQ query with variable where and select? - c#

I've got a LINQ query which looks like this:
var query = from produkt in Entity.ProduktCollection.produktCollection
let p = produkt as Entity.Produkt
from version in p.version
let v = version as Entity.Version
from customer in v.customerCollection
let c = customer as Entity.Customer
from fehler in v.fehlerCollection
let f = fehler as Entity.Fehler
where f.id == GetGuid();
select p;
but what I really need is a way to make the f plus its property as well as the p variable, so I might change them to every other possible combination, for example:
where c.name == "frank"
select f;
or
where p.id == GetGuid2()
select c;
Is there any way to achieve this? As far as I know there is no way to insert a switch-case block inside the query between the from/let-part and the where/select-part.

You can create your query in multiple statements because LINQ queries execution is deferred. It applies nicely to different Where statements situation:
var querySource = from produkt in Entity.ProduktCollection.produktCollection
let p = produkt as Entity.Produkt
from version in p.version
let v = version as Entity.Version
from customer in v.customerCollection
let c = customer as Entity.Customer
from fehler in v.fehlerCollection
let f = fehler as Entity.Fehler
select new { p, v, c, f };
if(/* first condition */)
{
querySource = querySource.Where(x => x.f.id == GetGuid());
}
else if(/* second condition */)
{
querySource = querySource.Where(x => x.p.id = 34);
}
var query = querySource.Select(x => x.p);
You can make the Select part conditional as well, but because returned IEnumerable<T> will differ in T part, you won't be able to assign them all to the same variable.

Related

Linq entity look ahead to value in next row

When querying my entity framework DB, I am trying to match a condition based on the values from 2 rows.
My current query looks like this:
var query = context.table.where(x => x.row > 2);
This all works fine.
What I now want to achieve is to query the table based on the value in current and next row eg:
var query = context.table.where(x => x.row > 2 && x.row[next row up in DB] < 2);
Can this be done.
I know I can achieve this in code, but can it be done in a single query using LINQ and entity?
Here is an example of how I would do this with SQL:
SELECT *
FROM t_Table p
INNER JOIN t_Table f
ON (p.id + 1) = f.id
WHERE p.column = whatever
AND f.column = whatever2
Translating your sample SQL into LINQ to SQL:
var ans = from p in t_table
from f in t_table
where (p.id+1) == f.id && p.column == whatever && f.column == whatever2
select new { p, f };
This does not appear to generate an inner join in SQL but rather a cross-join, but I assume the SQL engine will handle it appropriately. Note that LINQ can only do equi-joins.
I didn't realize you can do (some) expressions in LINQ joins as long as equal is the primary operator, and this generates a sub-select and an inner join, which seems quite a bit faster:
var ans = from p in t_table
where p.column == whatever
let pidplus1 = p.id+1
join f in t_table on pidplus1 equals f.id
where f.column == whatever2
select new { p, f };

Write sql query to linq

I am having following query in sql :
SELECT [definition],[pos]
FROM [WordNet].[dbo].[synsets]
where synsetid in(SELECT [synsetid] FROM [WordNet].[dbo].[senses]
where wordid = (select [wordid]FROM [WordNet].[dbo].[words]
where lemma = 'searchString'))
I had tried this for sql to linq :
long x = 0;
if (!String.IsNullOrEmpty(searchString))
{
var word = from w in db.words
where w.lemma == searchString
select w.wordId;
x = word.First();
var sence = from s in db.senses
where (s.senseId == x)
select s;
var synset = from syn in db.synsets
where sence.Contains(syn.synsetId)
select syn;
But I am getting following error at sence.Contains()
Error1:Instance argument: cannot convert from
'System.Linq.IQueryable<WordNetFinal.Models.sense>' to
'System.Linq.ParallelQuery<int>'
Below code:
var sence = from s in db.senses
where (s.senseId == x)
select s;
Returns object of type: WordNetFinal.Models.sense, but in where sence.Contains(syn.synsetId) you are trying to search in it syn.synsetId which is an integer.
So you should change above code to:
var sence = from s in db.senses
where (s.senseId == x)
select s.senseId;
x seems to be of Word type, which is not the type of Id (probably int or long).
You're comparing an entire sense row with a synsetId, which is not correct. You're also splitting the original query into two separate queries by using First() which triggers an evaluation of the expression so far. If you can live with not returning an SQL error if there are duplicates in words, you can write the query as something like this;
if (!String.IsNullOrEmpty(searchString))
{
var wordIds = from word in db.words
where word.lemma == searchString
select word.wordId;
var synsetIds = from sense in db.senses
where wordIds.Contains(sense.wordId)
select sense.synsetId;
var result = (from synset in db.synsets
where synsetIds.Contains(synset.synsetId)
select new {synset.definition, synset.pos}).ToList();
}
The ToList() triggering the evaluation once for the entire query.
You could also just do it using a simpler join;
var result = (from synset in db.synsets
join sense in db.senses on synset.synsetId equals sense.synsetId
join word in db.words on sense.wordId equals word.wordId
select new {synset.definition, synset.pos}).ToList();

LINQ to SQL omit field from results while still including it in the where clause

Basically I'm trying to do this in LINQ to SQL;
SELECT DISTINCT a,b,c FROM table WHERE z=35
I have tried this, (c# code)
(from record in db.table
select new table {
a = record.a,
b = record.b,
c = record.c
}).Where(record => record.z.Equals(35)).Distinct();
But when I remove column z from the table object in that fashion I get the following exception;
Binding error: Member 'table.z' not found in projection.
I can't return field z because it will render my distinct useless. Any help is appreciated, thanks.
Edit:
This is a more comprehensive example that includes the use of PredicateBuilder,
var clause = PredicateBuilder.False<User>();
clause = clause.Or(user => user.z.Equals(35));
foreach (int i in IntegerList) {
int tmp = i;
clause = clause.Or(user => user.a.Equals(tmp));
}
var results = (from u in db.Users
select new User {
a = user.a,
b = user.b,
c = user.c
}).Where(clause).Distinct();
Edit2:
Many thanks to everyone for the comments and answers, this is the solution I ended up with,
var clause = PredicateBuilder.False<User>();
clause = clause.Or(user => user.z.Equals(35));
foreach (int i in IntegerList) {
int tmp = i;
clause = clause.Or(user => user.a.Equals(tmp));
}
var results = (from u in db.Users
select u)
.Where(clause)
.Select(u => new User {
a = user.a,
b = user.b,
c = user.c
}).Distinct();
The ordering of the Where followed by the Select is vital.
problem is there because you where clause is outside linq query and you are applying the where clause on the new anonymous datatype thats y it causing error
Suggest you to change you query like
(from record in db.table
where record.z == 35
select new table {
a = record.a,
b = record.b,
c = record.c
}).Distinct();
Can't you just put the WHERE clause in the LINQ?
(from record in db.table
where record.z == 35
select new table {
a = record.a,
b = record.b,
c = record.c
}).Distinct();
Alternatively, if you absolutely had to have it the way you wrote it, use .Select
.Select(r => new { a = r.a, b=r.b, c=r.c }).Distinct();
As shown here LINQ Select Distinct with Anonymous Types, this method will work since it compares all public properties of anonymous types.
Hopefully this helps, unfortunately I have not much experience with LINQ so my answer is limited in expertise.

Linq contains operator

This works fine:
var q = (from c in db.tblLiteCategorySpecs where CategoryIDs.Contains(c.CategoryID) select c.SpecID).Distinct().ToList();
var qq = (from s in db.tblSpecifications where q.Contains(s.id) select s);
However I now need to return another field in the first query:
var q = (from c in db.tblLiteCategorySpecs where CategoryIDs.Contains(c.CategoryID) select new { c.SpecID, c.FriendlyName }).Distinct().ToList();
var qq = (from s in db.tblSpecifications where q.Contains(s.id) select s);
So the q.contains now fails, I need this to somehow work on the q query SpecID field. Anyone know how to do this?
Well, you could try:
var qq = from s in db.tblSpecifications
where q.Select(x => x.SpecID).Contains(s.id)
select s;
In other words, project the result before using Contains. I've no idea what the SQL will look like.
By the way, I'd personally just write this as:
var qq = db.tblSpecifications
.Where(s => q.Select(x => x.SpecID).Contains(s.id));
I only use query expression syntax where it really makes things simpler. I'd also strongly encourage you to use multiple lines for the queries - it can really help with readability.
Is this what you want?
var q = (from c in db.tblLiteCategorySpecs where CategoryIDs.Contains(c.CategoryID) select new { c.SpecID, c.FriendlyName }).Distinct().ToList();
var qq = (from s in db.tblSpecifications where q.Any(c => c.SpecID == s.id) select s);

LINQ Queries And Context

I have a slight issue with some code I'm writing
if(parameter == 1)
{
var linq = from a in db.table select a;
}
else
{
var linq = from a in db.table where a.id = 1 select a;
}
foreach(var b in linq)
{
...
}
So basically what's going on is that the variable "linq" is different depending on the value of "parameter". When I try to loop through "linq" with my foreach loop, I get an error about how linq doesn't exist in the current context.
What is the best way to work around this type of issue?
What you tried doesn't work because the variable linq is already out of scope when you try to use it. You need to move the declaration to the outer scope.
To answer your question in a general way first: if you need to declare a variable before you assign to it, you can't use var. You need to declare the type explicitly:
IQueryable<Something> linq;
if(parameter == 1)
{
linq = from a in db.table select a;
}
else
{
linq = from a in db.table where a.id == 1 select a;
}
In your particular example though you can simplify things:
var query = from a in db.table select a;
if (parameter != 1)
{
query = query.Where(a => a.id == 1);
}
I dunno if this is the best way but, assuming you're returning the same table.
var linq = from a in db.table where a.id = 1 select a;
if(parameter == 1)
{
linq = from a in db.table select a;
}
//foreach.
You can reconstruct your linq query and not pay a big penalty, since you haven't actually executed it.

Categories

Resources