Join Lambda Expression - linq

im just trying to make a join from two entities.
the two entieties are as folows:
public partial class AnswerSet
{
public int Id { get; set; }
public string Ans { get; set; }
public bool IsTrue { get; set; }
public int QuestionId { get; set; }
public virtual QuestionSet QuestionSet { get; set; }
}
and
public partial class QuestionSet
{
public QuestionSet()
{
this.AnswerSets = new HashSet<AnswerSet>();
}
public int Id { get; set; }
public string Quest { get; set; }
public virtual ICollection<AnswerSet> AnswerSets { get; set; }
}
So, there are a question and an Answer Entity on the database. 1 Question has more answers (in my example 4). So now im tried this:
var v1 = db.QuestionSets
.Select(q => q)
.Where(q=> q.Id == 11)
.Join(db.AnswerSets,
q => q.Id,
a => a.QuestionId,
(a, q) => new { question = q });
So, now i have the following output when the expression is as above (see image 1):
Here i have just the Answers.
when i chage the expression to:
var v1 = db.QuestionSets
.Select(q => q)
.Where(q=> q.Id == 11)
.Join(db.AnswerSets,
q => q.Id,
a => a.QuestionId,
(q, a) => new { question = q });
then i have the following output (see image 2): (just the question, but 4 times. as many answers i have.)
So my question is, how can i join these two entities, that the Answers are a set in the QuestionSet entity?
thank you
Image 1
Image 2

You want a group join (see http://msdn.microsoft.com/en-us/library/bb311040.aspx)
var QAs =
from q in db.QuestionSets
join a in db.AnswerSets
on q.Id equals a.QuestionId into answers
select new { Question = q, Answers = answers };
In Extension Method syntax:
var QAs = db.QuestionSets
.GroupJoin(
db.AnswerSets,
q => q.Id,
a => a.QuestionId,
(q, answers) => new {Question = q, Answers = answers});
Hope that helps
PS. Use it like so:
foreach (var qa in QAs)
{
Console.WriteLine("Question #{0} has {1} answers",
qa.Question.Id, qa.Answers.Count());
}

What about leaving the Join technique for complex query and use Ef Eager Loading by using Include
var v1 = db.QuestionSets.Include(b = b.Answer);
or
var v1 = db.QuestionSets.Include("Answer");

Related

How to do an OR in LINQ from statement

I am trying to write a LINQ query where I want to access data from two different tables depending on what the front end sends back
if FlowerClients is being sent from the front end, I want to use FlowerClient table OR if data being sent is for AnotherClient use AnotherClient.
Is this possible to do in LINQ or is it better to create different methods?
var flowerById = (from flower in flowerContext.Flowers
where flower.FlowerId == flowerId
join petals in flowerContext.Petals on flower.PetalId equals petals.PetalId
from flowerClients in flowerContext.FlowerClients.Where(x => x.FlowerId == flower.FlowerId).DefaultIfEmpty()
||
from anotherFlowerClients in flowerContext.AnotherClients.Where(x => x.FlowerId == flower.FlowerId).DefaultIfEmpty()
You'll probably want to create different methods, particularly if the FlowerClients and AnotherClients classes are very different.
Otherwise, you could dynamically generate your query expression. You will probably want to make sure that FlowerClients and AnotherClients implement a common interface however that way you don't have to do any sort of mapping between the two.
IQueryable<FlowerPetal> query1 = flowerContext.Flowers
.Where(f => f.FlowerId == flowerId)
.Join(flowerContext.Petals,
f => f.PetalId,
p => p.PetalId,
(f, p) => new FlowerPetal { Flower = f, Petal = p }
);
IQueryable<FlowerPetalClient> WithClients(IQueryable<FlowerPetal> _query, IQueryable<IClient> _clients) =>
_query.SelectMany(
x => _clients.Where(c => c.FlowerId == x.Flower.FlowerId).DefaultIfEmpty(),
(x, c) => new FlowerPetalClient { Flower = x.Flower, Petal = x.Petal, Client = c }
);
IQueryable<IClient> clients = useFlowerClients // decide on which client to use
? flowerContext.FlowerClients
: flowerContext.AnotherClients;
var query = WithClients(query1, clients); // expand on query
with some helper classes:
class FlowerPetal
{
public Flower Flower { get; set; }
public Petal Petal { get; set; }
}
class FlowerPetalClient
{
public Flower Flower { get; set; }
public Petal Petal { get; set; }
public IClient Client { get; set; }
}

LINQ Query to join three tables

I need a help in LINQ Query for the below.
public interface IBrand
{
int BrandId { get; set; }
IEnumerable<IBuyingAgency> BuyingAgencies { get; set; }
}
public interface IBuyingAgency
{
int BuyingAgencyId { get; set; }
}
public interface IClientGroup
{
IBuyingAgency BuyingAgency { get; set; }
int ClientGroupId { get; set; }
}
1). var brands = LoggedInUserHelper.GetUser().GetBrands(roles); // returns IEnumerable<Tuple<IBrand, string>>
2). var buyingAgencies = LoggedInUserHelper.GetUser().GetBuyingAgencies(roles); //IEnumerable<IBuyingAgency>
3). var clientGroups = LoggedInUserHelper.GetUser().GetClientGroups(roles); //IEnumerable<IClientGroup>
function IEnumerable<IClientGroup> GetClientGroups( List<int> BrandIds)
{
var brands = LoggedInUserHelper.GetUser().GetBrands(roles); // returns IEnumerable<Tuple<IBrand, string>>
var buyingAgencies = LoggedInUserHelper.GetUser().GetBuyingAgencies(roles); //IEnumerable<IBuyingAgency>
var clientGroups = LoggedInUserHelper.GetUser().GetClientGroups(roles); //IEnumerable<IClientGroup>
var lstBrandagencies = brands.Where(brand => BrandIds.Contains(brand.Item1.BrandId) && brand.Item1.BuyingAgencies.Any( ba => buyingAgencies.Contains(ba.BuyingAgencyId))).SelectMany(brand => brand.Item1.BuyingAgencies);
var buyingAgencyIDs = lstBrandagencies.Select(b => b.BuyingAgencyId);
clientGroups = clientGroups.Where(cg => buyingAgencyIDs.Contains(cg.BuyingAgency.BuyingAgencyId));
return Mapper.Map<IEnumerable<IClientGroup>>(clientGroups.ToList());
}
I wrote the above function but not working, it gets all the clientgroups instead of filtering
I wants to write a query to get all ClientGroups whch satisfies the below condition
1. retrieve the brand from brands ( above ) that matches the list of brandId's passing in as parameter
2. Than get all the buyingAgencies under brands (1) above which matches with the id's of (2) above
3. Finally get all clientgroups which matches with the buyingAgency retrieving in step (2)
Please can you help.
you are not filtering from your source 2) in this line
var buyingAgencyIDs = lstBrandagencies.Select(b => b.BuyingAgencyId);
just projecting from the previous query.
If I understood correctly you want to do this.
var lstBrandagencies = (from a in brands
where BrandIds.Contains(a.Item1.BrandId )
select a).SelectMany (b => b.Item1.BuyingAgencies )
.Select (b => b.BuyingAgencyId );
var buyingAgencyIDs = from a in buyingAgencies
where lstBrandagencies.Contains(a.BuyingAgencyId )
select a.BuyingAgencyId;
var clientGroupsResult = clientGroups.Where(cg => buyingAgencyIDs.Contains(cg.BuyingAgency.BuyingAgencyId));

IQueryable to get sum and order by descending in subset

I have a collection of DTO's in which I want to do some ordering by the sum of some values in a nested DTO.
The hiearchy is as follows:
Its a collction of QuestionDTO's.
A QuestionDTO has Many Answers which has Many Votes.
So in short:
1 QuestionDto: QuestionID, QuestionTitle, ANSWERS: AnswerId, Answer, VOTES: VoteId, AnswerId, Value
Its the last value that I want to have summed for each answer, and then order by this sum for each question. Making the most popular question/answer on top of the list.
Thanks in advance
I think you want this:
var questionList = new List<Question>();//Get a real list
var sortedQuestions = (from i in questionList
select new
{
i.QuestionID,
i.QuestionText,
VotesSum = i.Answers.Sum(ee => ee.Votes.Sum(ss => ss.Value))
}
).OrderByDescending(ee => ee.VotesSum);
foreach (var item in sortedQuestions)
Console.WriteLine(item.QuestionText + " " + item.VotesSum);
Asumming your classes are like these
class Vote
{
public int VoteID { get; set; }
public int Value { get; set; }
}
class Answer
{
public int AnswerID { get; set; }
public string AnswerText { get; set; }
public List<Vote> Votes = new List<Vote>();
}
class Question
{
public int QuestionID { get; set; }
public string QuestionText { get; set; }
public List<Answer> Answers = new List<Answer>();
}
Here's a way to get what you want:
var query = questions.Select(q =>
new { q.QuestionText,
Answers = q.Answers.Select(a =>
new { a.AnswerText,
Votes = a.Votes.Sum(v => v.Value)
})
})
.Select(q =>
new { q.QuestionText,
Votes = q.Answers.Sum(a => a.Votes),
Answers = q.Answers.OrderByDescending(a => a.Votes)
})
.OrderByDescending(q => q.Votes);
As you see, the votes are summed at two levels, Answer and Question and sorted in descending order.

how to practically assign repeating objects from groups

I am having a difficult time finding a proper Linq query to utilize the group output.
I want to populate an existing students List where Student class has 2 properties ID and and int[] Repeats array (can be a list too) to keep how many times they took any of the 4 lectures (L101,L201,L202,L203). So if student takes L101 twice, L202 and L203 once, and but didn't take L201 this should be {2,0,1,1,}
class Student{
public string ID{get;set;}
public int[] Repeats{get;set;} //int[0]->L101, int[1]->L201...
}
In my main class I do this basic operation for this task:
foreach (var student in students)
{
var countL101 = from s in rawData
where student.Id==s.Id & s.Lecture =="L101"
select; //do for each lecture
student.Repeats = new int[4];
student.Repeats[0] = countL101.Count(); //do for each lecture
}
This works; but I wonder how do you make it practically using Linq in case where there are 100s of lectures?
I am using Lamba Expressions rather than query syntax. Then assuming rawData is IEnumerable<T> where T looks something like...
class DataRow
{
/// <summary>
/// Id of Student taking lecture
/// </summary>
public string Id { get; set; }
public string Lecture { get; set;}
}
Then you could do something like...
var lectures = rawData.Select(x => x.Lecture).Distinct().ToList();
int i = 0;
lectures.ForEach(l =>
{
students.ForEach(s =>
{
if (s.Repeats == null)
s.Repeats = new int[lectures.Count];
s.Repeats[i] = rawData.Count(x => x.Id == s.Id && x.Lecture == l);
});
i++;
});
Now if Repeats could just be of type IList<int> instead of int[] then...
var lectures = rawData.Select(x => x.Lecture).Distinct().ToList();
lectures.ForEach(l =>
{
students.ForEach(s =>
{
if (s.Repeats == null)
s.Repeats = new List<int>();
s.Repeats.Add(rawData.Count(x => x.Id == s.Id && x.Lecture == l));
});
});
Things are further simplified if Repeats could just be instantiated to a new List<int> in the Student constructor...
class Student
{
public Student()
{
Repeats = new List<int>();
}
public string Id { get; set; }
public IList<int> Repeats { get; private set; }
}
Then you can do it in one line...
rawData.Select(x => x.Lecture).Distinct().ToList()
.ForEach(l =>
{
students.ForEach(s =>
{
s.Repeats.Add(rawData.Count(x => x.Id == s.Id && x.Lecture == l));
});
});

How to use dynamic linq to query through nested arrays?

If I have class structure as follows:
private class A
{
int afoo { get; set; }
B[] bList { get; set; }
}
private class B
{
String bfoo { get; set; }
C[] cList { get; set; }
}
private class C
{
String cfoo { get; set; }
int cfoo2 { get; set; }
}
public class Master
{
A[] aList;
//qry for all where A.B.C.cfoo = "test"
}
How would I construct a dynamic LINQ statement to query for items in class C? Something like this
1) var qry = aList.blist.clist.AsQueryable().Where("cfoo = \"Test\"").Select();
My ultimate solution would be to pass the entire path in the dynamic part like this:
2) var qry = aList.AsQueryable().Where("bList.cList.cFoo = ""Test"").Select();
But from what I have tried you can not have the nested objects in the Where. So I am going to live with using templates to build the methods as in 1) above.
[Also, I am using the dynamic library from Scott Gu for the dynamic part.]
But, I can't get that to work. Any suggestions?
Thanks!
aList.SelectMany(a => a.bList)
.SelectMany(b => b.cList)
.Where(c => c.cfoo == "\"Test\"");
Try this:
aList.SelectMany(a => a.bList.SelectMany(b => b.cList))
.Where(c => c.cf002 == "Test")
.Select();

Resources