has-relation in linq-statement? - c#

I have three tables. Question, Discipline and QuestionHasDiscipline. QuestionHasDiscipline holds the relation between Question and Discipline. They all have an unique id-column to identify them.
I am trying to write a linq-statement that returns all the questions that have a certain discipline.
What I have begun doing is this:
var questions = (from q in context.Questions
where (from d in context.QuestionHasDiscipline
where d.QuestionId == q.QuestionId
) ...
But it obviously is horribly wrong. I've tried different approaches but now I turn to the greater minds.. Any suggestions?

You can use .Any() with a predicate.
from q in context.Questions
where context.QuestionHasDiscipline.Any(d => d.QuestionId == q.QuestionId)
select q;

Related

Grouping with child sort

I'm trying to get a list of questions for a survey where the order of the questions is stored in the intersection table of the many to many relationship.
My questions are grouped into sections as well, and everything works except the sort. It doesn't throw an error, but it doesn't sort the children (questions).
Here is my query:
from surveyQuestion in Context.SurveyQuestions
join question in Context.Questions
on surveyQuestion.Question_ID equals question.Question_ID
where surveyQuestion.Survey_ID == surveyId
orderby surveyQuestion.SORT_ORDER
group question by surveyQuestion.SECTION into section
select new SurveySection
{
SurveyId = surveyId,
SectionId = section.Key,
Questions = section.ToList()
};
I have tried adding .OrderBy to the section before the list, but in that context, the order has been lost, and I only have the question left. I can't put the sort order in the question table, since the questions are reused and may have different sort order values.
I figured out one way to make it work, but it seems like it might be the wrong way. I created a new anonymous object including my sort and the child object, then sorted on that, then selected the child object into a list.
Now my query looks like this:
from surveyQuestion in Context.SurveyQuestions
join question in Context.Questions
on surveyQuestion.Question_ID equals question.Question_ID
where surveyQuestion.Survey_ID == surveyId
orderby surveyQuestion.SORT_ORDER
group new { question, surveyQuestion.SORT_ORDER } by surveyQuestion.SECTION into section
select new SurveySection
{
SurveyId = surveyId,
SectionId = section.Key,
Questions = section.OrderBy(q => q.SORT_ORDER)
.Select(q => q.question)
.ToList()
};
Is there a better way?
Try
from surveyQuestion in Context.SurveyQuestions
join question in Context.Questions
on surveyQuestion.Question_ID equals question.Question_ID
where surveyQuestion.Survey_ID == surveyId
orderby surveyQuestion.Question_ID,surveyQuestion.SORT_ORDER
group question by surveyQuestion.SECTION into section
select new SurveySection
{
SurveyId = surveyId,
SectionId = section.Key,
Questions = section.ToList()
};

Linq to objects - boondoggle?

I thought that LINQ to objects is great way for differed execution of joined data. In reality, it comes up as bad way to do things...
Here is what we had, having few, few hundred, 3K and 3.5K records in a,b,c,d correspondingly
IEnumerable<MyModel> data =
(from a in AList
from b in BList.Where(r => r.AId == a.Id)
from c in CList.Where(r => r.BId == b.Id)
from d in DList.Where(r => r.SomeId == myId && r.Some2Id == c.Some2Id)
// . . . . . .
Wasn't LINQ supposed to be great about doing it?
In reality, following works much faster, 60 times faster in fact
var dTemp = DList.Where(r => r.SomeId == myId).ToList();
var cTemp = CList.Where(c => dTemp.Any(d => d.Some2Id == c.Some2Id)).ToList();
IEnumerable<MyModel> data =
(from a in AList
from b in BList.Where(r => r.AId == a.Id)
from c in cTemp.Where(r => r.BId == b.Id)
// . . . . . .
And then I came across this article
Q: Is there a way to improve this query without abandoning single LINQ?
Or does this mean that LINQ to objects in form of joins need to be avoided and replaced by some sequential calls if performance is at stake?
Let's analyze the differences.
First query: you are performing a filter on BList, a filter on CListand two filters on DList, all in a deferred-execution manner. You then use a kind of a join.
Second query: you perform a static filter on DList and evaluate it, another static filter on CList based on DList and evaluate it and then a deferred-executed filter on both AList and BList.
The second query is faster because:
DList is not being looked at for useless values (due to previous filters)
CList only contains useful values due to previous filters
Anyway, both queries are wrong. Multiple from is basically a cross-join, as explained here. As #Reddog commented, the best way is to actually use Join:
var data = from a in AList
join b in BList on a.Id equals b.AId
join c in CList on b.Id equals c.BId
join d in DList on c.Some2Id equals d.Some2Id
where d.SomeId == someId;

How to use EXISTS in where condition of LINQ?

I have gone through couple of similar questions but couldn't find what I wanted.
I need to find tags which has a ID in Answers table, since there could be multiple answers records containing the tag ID I need to take care of the duplicates. This is the reason why I cant perform simple join.
I thought about using an EXISTS but couldn't figure out the way to do it yet.
This is the working SQL
SELECT DISTINCT TagName
FROM Tags tag
JOIN Answers ans ON ans.StID = tag.Id
WHERE tag.SchId = 472
AND ans.isValid = 1
This is what I tried in LINQ with no success
(from tag in Tags
where tag.Id.Any(from ans in Answers
where ans.StID == tag.Id
&& tag.SchId == 472
&& ans.isValid == true
select ans.ID)
select tag.TagName
It would be helpful if someone can suggest a way to correctly implement this query in LINQ.
You'll generally want to accomplish this by using the Enumerable.Any() method to pass in a function that will check if any items in your collection meet a given criteria :
var x = Tags.Where(t => t.SchId == 472 && Answers.Any(a => a.StId == tag.Id && a.isValid))
.Select(t => t.TagName)
.Distinct();
This should give you a list of distinct tags that have a SchId of 472 and have at least one valid answer.
Look into the Distinct method which will get rid of your duplicates. Try the code below.
(from tag in Tags
where tag.Id.Any(from ans in Answers
where ans.StID == tag.Id
&& tag.SchId == 472
&& ans.isValid == true
select ans.ID).Distinct(ans => ans.ID)
select tag.TagName

Linq join on two values

Suppose I have a list of {City, State}. It originally came from the database, and I have LocationID, but by now I loaded it into memory. Suppose I also have a table of fast food restaurants that has City and State as part of the record. I need to get a list of establishments that match city and state.
NOTE: I try to describe a simplified scenario; my business domain is completely different.
I came up with the following LINQ solution:
var establishments = from r in restaurants
from l in locations
where l.LocationId == id &&
l.City == r.City &&
l.State == r.State
select r
and I feel there must be something better. For starters, I already have City/State in memory - so to go back to the database only to have a join seems very inefficient. I am looking for some way to say {r.City, r.State} match Any(MyList) where MyList is my collection of City/State.
UPDATE
I tried to update based on suggestion below:
List<CityState> myCityStates = ...;
var establishments =
from r in restaurants
join l in myCityStates
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
and I got the following compile error:
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
UPDATE 2
Compiler didn't like anonymous class in the join. I made it explicit and it stopped complaining. I'll see if it actually works in the morning...
It seems to me that you need this:
var establishments =
from r in restaurants
join l in locations.Where(x => x.LocationId == id)
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
Well, there isn't a lot more that you can do, as long as you rely on a table lookup, the only thing you can do to speed up things is to put an index on City and State.
The linq statement has to translate into a valid SQL Statement, where "Any" would translate to something like :
SELECT * FROM Restaurants where City in ('...all cities')
I dont know if other ORM's give better performance for these types of scenarios that EF, but it might be worth investigating. EF has never had a rumor for being fast on reads.
Edit: You can also do this:
List<string> names = new List { "John", "Max", "Pete" };
bool has = customers.Any(cus => names.Contains(cus.FirstName));
this will produce the necessary IN('value1', 'value2' ...) functionality that you were looking for

Modularize (refactor) Linq queries

I have a few Linq queries. Semantically, they are
a join b join c join d where filter1(a) && filter2(c) && filter3(d)
a join b join c where filter1(a) && filter2(c)
a join b join c join e where filter1(a) && filter2(c) && filter4(e)
...
I want to be able to factor out the shared part:
a join b join c where filter1(a) && filter2(c)
and dynamically append join d and filter3(d)
Is there a way to do this? I am already using the Predicate Builder to dynamically build conditionals (filters).
EDIT: I am using Linq-to-SQL.
EDIT: The base query looks like:
from a in As.AsExpandable()
join b in Bs on a.Id equals b.PId
join c in Cs on b.Id equals c.PId
where filter1(a) && filter2(b) && filter3(c)
select new A { ... }
filters are predicates in Predicate Builder. The type of the query is IQueryable<A>.
Next, I'd like to join this with d
from a in BaseQuery()
join d in D on a.Id equals d.PId
Currently join d .. causes a compilation error:
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to Join
Your example is a bit vague, but it is easy to create a method that returns an IQueryable<T> and reuse that method, if that’s what you mean. Here is an example:
// Reusable method
public IQueryable<SomeObject> GetSomeObjectsByFilter(Context c)
{
return
from someObject in context.SomeObjects
where c.B.A.Amount < 1000
where c.Roles.Contains(r => r.Name == "Admin")
select someObject;
}
You can reuse this method in other places like this:
var q =
from c in GetSomeObjectsByFilter(context)
where !c.D.Contains(d => d.Items.Any(i => i.Value > 100))
select c;
Because the way IQueryable works, only the final query (the collection that you start iterating) will trigger a call to the database, which allows you to build a highly maintainable system by reusing business logic that gets effectively executed inside the database, whiteout the loss of any performance.
I do this all the time and it improves the maintainability of my code big time. It works no matter which O/RM tool you run, because there is no difference in Queryable<T> composition, between writing the query in one peace, or splitting it out to different methods.
Note that you do sometimes need some smart transformations to get the duplicate parts in a single method. Things that might help are returning grouped sets, and returning a set of a different type, than what you think you need. This sounds a bit vaque, but just post a question here at SO when you have problems splitting up a method. There are enough people here that can help you with that.
I can answer half your question easily. Linq makes it simple to append .where clauses to an existing query.
Example:
var x = db.Table1.where(w => w.field1 == nTestValue);
x = x.where(w => w.field2 == nTestValue2);
I believe you can do the joins as well but I have to go find an example in some old code. I'll look if nobody else jumps in with it soon.

Categories