Linq where clause 2 condition order? - c#

i tried this linq code for get questions
return (from i in db.Question
where i.Id == questionId || (i.RelatedId == questionId && i.IsAccept == isAccept)
select i)
.ToList();
I got 2 question but their order was wrong. Can I say to Linq get first where clause then second condition?
Note: First get i.Id == questionId then get second condition rows
Note2: Id and RelatedId is Guid. I am trying to do this without order by and single query

You can keep a variable which shows the returned row, satisfies which of your conditions. Then select the row together with this variable(here named as 'order') in an anonymous type. Finally, OrderBy your results based on your variable and Select your row.
return (from i in db.Question
where i.Id == questionId || (i.RelatedId == questionId && i.IsAccept == isAccept)
let order = i.Id == questionId ? 1 : 0
select new {i, order}).Orderby(a => a.order).Select(a => a.i)
.ToList();

For database queries you can use asynchronous queries and combine result later, which makes code little bid more comprehensible and asynchronous queries will be executed almost simultaneously.
var byIdTask =
db.Question.Where(question => question.Id == questionId).ToListAsync();
var relatedTask =
db.Question.Where(question => question.RelatedId = questionId)
.Where(question => question.IsAccept == isAccept)
.ToListAsync();
await Task.WhenAll(new[] byIdTask, relatedTask);
var allQuestions = byIdTask.Result.Concat(relatedTask.Result).ToList();

i tried this and looking work
List<Question> final = new List<Question>();
var result = (from i in db.Question where i.Id == questionId || (i.RelatedId == questionId && i.IsAccept == isAccept) select i).ToList();
final.Add(result.Where(a => a.Id == questionId).SingleOrDefault());
final.Add(result.Where(a => a.Id != questionId).SingleOrDefault());
im getting all with single query and adding them to list with conditions

Related

How can I use optional parameters in a query with multiple joins, using Entity Framework?

I've looked at several possible solutions to this problem, and the ones I have tried do not seem to work. One solution was to use if statements for the optional filters, which doesn't work because I have multiple joins and the where clause is in the last join.
The optional parameters are: roleId, disciplineId, resourceId, and projectName.
try
{
IQueryable<ProjectPlanHeader> bob =
(
from h in context.ProjectPlanHeaders
join r in context.ProjectPlanRevisions on h.ProjectPlanHeaderId equals r.ProjectPlanHeaderId
join a in context.PlanActivityLineItems on r.PlanRevisionId equals a.PlanRevisionId
where ((roleId == null || a.RequiredRoleId == roleId) &&
(disciplineId == null || a.DisciplineId == disciplineId) &&
(resourceId == null || a.ActualResourceId == resourceId) &&
(h.ProjectPlanName.ToLower().Contains(projectName.ToLower()) || projectName == String.Empty))
select h
)
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem)
.ThenInclude(z => z.PlannedHours)
.Include(x => x.ActualPlanRevisions)
.ThenInclude(y => y.ActualPlanActivities)
.ThenInclude(z => z.ActualHours);
var john = bob.ToList();
return bob;
}
catch (Exception ex)
{
return null;
}
I added the try/catch so I could see what was happening, as it was silently failing. What I found was a "Object not set to an instance of an object". That's never helpful, because I don't know what object it's talking about. Can someone please show me how to do this the right way?
UPDATE: Thanks for the responses I got, but unfortunately they don't work. The problem is that I end up getting multiple headers back when I filter. This happens because there are multiple revisions for each header, and I really only need the max rev. I tried changing the initial query so that only the max rev was included, and that still did not help. There does not appear to be a solution for this issue, so I will have to do it another way.
Rewrite query to do not use explicit joins, because you have navigation properties. Also because of JOINS you have duplicated records, you will discover it later.
var query = context.ProjectPlanHeaders
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem)
.ThenInclude(z => z.PlannedHours)
.Include(x => x.ActualPlanRevisions)
.ThenInclude(y => y.ActualPlanActivities)
.ThenInclude(z => z.ActualHours)
.AsQueryable();
if (!string.IsNullOrEmpty(projectName))
{
// here we can combine query
query = query
.Where(h => h.ProjectPlanName.ToLower().Contains(projectName.ToLower()));
}
// check that we have to apply filter on collection
if (roleId != null || disciplineId != null || resourceId != null)
{
// here we have to do filtering as in original query
query = query
.Where(h => h.ProjectPlanRevisions
.Where(r => roleId == null || r.PlanActivityLineItem.RequiredRoleId == roleId)
.Where(r => disciplineId == null || r.PlanActivityLineItem.DisciplineId == disciplineId)
.Where(r => resourceId == null || r.PlanActivityLineItem.ActualResourceId == resourceId)
.Any()
);
}
var result = query.ToList();
Let me clarify my comment with an example:
So: An IQueryable is used to build up an expression tree. They are not evaluated until enummerated (e.g. ToList or FirstOrDefault). I.e. you can conditional add Where and Includes with little to no cost before ennumerating`
Thus you could do this,
IQueryable<ProjectPlanHeader> bob =
context.ProjectPlanHeader
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem);
if (roleId != null) {
bob =
from h in bob
join r in context.ProjectPlanRevisions on h.Id equals r.ProjectPlanHeaderId
join a in context.PlanActivityLineItems on r.Id equals a.ProjectPlanRevisionId
where a.RequiredRoleId == roleId
select h;
}
// same for the rest.
var john = bob.ToList();
writing the chained filer is not easiest, but it works

IQueryable with distinct records and ordered

I've been trying to get an IQueryable with distinct and ordered values, but I've found that I can't apply a distinct after orderby or I'll lose the order.
The last query I tried was the following:
IQueryable<gbd_Pages> Listpagespages =
(from c in _db.gbd_Content
where c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder
orderby c.Content ascending
select c.gbd_Pages);
With this I get repeated results.
The table I want returned is gbd_Pages which has a relation of 1 to many with gbd_Content.
With this i mean that gbd_Content will have a foreign key that will have the primary key of gbd_Pages.
I need to do a sortOrder by the table gbd_Template_Fields wich has a relation 1 to 1 with gbd_Content.
Is there a way for me to do this? I need it to be an IQueryable without converting to IEnumerable or list.
I believe what you need is something like this:
var query =
from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder
orderby c.Content ascending
select c).Take(1)
orderby pc.Content ascending
select p;
So you start from one side of the relationship (to avoid the need of Distinct), then you select a single record from the many side matching the criteria and having the smaller value of the sorting field (using ordered subquery + Take(1)), and finally sort the result using the sort field value from that single child record.
I'm assuming you have inverse collection navigation property from gbd_Pages to gbd_Content. If you don't, replace the p.gbd_Content with _db.gbd_Content where c.[gbd_Content_FK] == p.[PK].
Your primary from needs to be the gbd_Pages table, rather than gbp_Content if those are the results that you want to return. I'll have to assume a foreign key here but you'd want to change to something like;
IQueryable<gbd_Pages> Listpagespages = _db.gbp_Pages
.Where(p => (from c in _db.gbd_Content
where c.IsActive == true
&& c.IsDeleted == false
select c.gbd_Pages.PrimaryKeyID)
.Any())
.Select(p => new
{
// select specific fields here...
p,
SortCol = _db.gbp_Content
.FirstOrDefault(c => c.PrimaryKeyID)
.Where(c => c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder)
.Select(c => c.Content)
})
.OrderBy(v => c.SortCol);
Try this:
IQueryable<gbd_Pages> Listpagespages = _db.gbd_Content
.Select (c=>new { c.gbd_Pages })
.Where(c=>c.IsActive == true && c.IsDeleted == false &&c.gbd_Template_Fields.SortOrder == sortOrder)
.Distinct()
.OrderBy(c => c.Content)
.Select(c => c.gbd_Pages)

LINQ query from two related lists with specific criteria

Using SQL Server and C#:
I have 3 tables: Employee (EmployeeId, JobDescription), CashAllowance (CashAllowanceId) and EmployeeCashAllowance (EmployeeId, CashAllowanceId, ValueTaken, DateAdded).
Employee has (EmployeeeId) as primary key, CashAllowance has (CashAllowanceId) as primary key, EmployeeCashAllowance has 2 foreign keys (EmployeeId and CashAllowanceId) related to the first 2 tables.
I need to get the list of (EmployeeCashAllowance) in a specific date + for specific CashAllowanceId + for employees having JobDescription = "Dev"
I need to achieve this in a LINQ query on lists filled from DB where list of all EmployeeCashAllowance is a property of the Employee object (each Employee object has List ListEmployeeCashAllowances as a property). What I wrote was this:
var sumValues = (from e in Employees
where (e.JobDescription == "Dev")
from c in e.ListEmployeeCashAllowances
where (c.EmployeeId == e.EmployeeId && c.CashAllowanceId == selectedCashAllowanceId && c.DateAdded == selectedDate)
select c).ToList();
But this is not working as I expected, it's returning all rows in Employee and EmployeeCashAllowance whatever the selected criteria is (even is JobDescription is not Dev and CashAllowanceId is not the selectedCashAllowanceId).
Where did I go wrong with this?
You better use join I suppose try something like this , I haven't tested so it must look like this
var sumValues = (from e in Employees
join c in EmployeeCashAllowances on e.EmployeeId equals c.EmployeeId
where ( c.CashAllowanceId == selectedCashAllowanceId && c.DateAdded == selectedDate && e.JobDescription == "Dev")
select c).ToList();
Not Tested, Just gave a try
var results = from e in Employees
from ec in e.EmployeeCashAllowances
where (e.EmployeeId == ec.EmployeeId && ec.CashAllowanceId == selectedCashAllowanceId && ec.DateAdded == selectedDate && e.JobDescription == "Dev")
select ec;
Well, I didn't expect this, but I had to change from the query to get what I want, with the restriction that I had use "Sum" and get sum of values in the list of EmployeeCashAllowance resulted:
Employees.SelectMany(e => e.ListEmployeeCashAllowances).Where(lc => lc.CashAllowanceId == selectedCashAllowanceId).Select(c => c.ValueTaken).Sum();
Note that if I don't use Sum the list returned will contain all items in all the ListEmployeeCashAllowances.
It becomes clear when you clean the formatting:
var sumValues =
from e in Employees
where
e.JobDescription == "Dev"
from c in e.ListEmployeeCashAllowances // Gotcha!!!
where
c.EmployeeId == e.EmployeeId && // Is that really necessary?
c.CashAllowanceId == selectedCashAllowanceId &&
c.DateAdded == selectedDate
select c;
Normally I use LINQ methods like:
var query = Employees
.Where(e => e.JobDescription == "Dev")
.SelectMany(e => e.ListEmployeeCashAllowances)
.Where(c =>
c.CashAllowanceId == selectedCashAllowanceId &&
c.DateAdded == selectedDate);
But 'e.ListEmployeeCashAllowances' still can be selecting all users...
A long shot, without knowing your environment, could be:
var query = Employees
.SelectMany(e => e.ListEmployeeCashAllowances)
.Where(c =>
c.JobDescription == "Dev" &&
c.CashAllowanceId == selectedCashAllowanceId &&
c.DateAdded == selectedDate);
Resolving what to do with 'query', you can simply do:
var sumValues = query.Sum(c => c.ValueTaken);

LINQ to Entity join query

I have the following setup:
Table ShoeAreas that has columns ShoeId and MaterialId.
Table Shoes that has columns ID and Status.
I have a method that takes one argument - materialId and the goal is to determine if there is a record in ShoeAreas with a MaterialId equal to the one passed like an argument. And if such a record (or records most probably) exist if they are relateed to shoe from Shoes withStatus` = Production.
I tried this :
return shoeService.All().
Join(shoeAreaService.All(),
s => s.ID,
sa => sa.ShoeId,
(s, sa) => (sa.MaterialId == matId)).
Any(s => (s.Status == (byte)EntityStatusProd.Production)));
But I get error on the Any.. line saying } expected and also this is my second Linq to Entity query that I write so I have doubts if it's syntax problem or the query is wrong itself.
You are returning IEnumerable<bool> from Join method (values of condition sa.MaterialId == matId). Create anonymous type which will hold both joined entities instead:
return shoeService.All()
.Join(shoeAreaService.All(),
s => s.ID,
sa => sa.ShoeId,
(s, sa) => new { s, sa }) // here
.Any(x => (x.sa.MaterialId == matId) &&
(x.s.Status == (byte)EntityStatusProd.Production)));
you can try this: (linq )
from shoe in Shoes
join shoeArea in ShoesArea on shoe.ID equals shoeArea.ShoeID
where shoeArea.MeterialID == matID && shoe.Status == (byte)EntityStatusProd.Production
select new {shoe.ID,shoe.Status};
return shoeService.All().Any(s => shoeAreaService.All()
.Any(sa => sa.MaterialId == matId
&& s.Id == sa.ShoeId)
&& s.Status == (byte)EntityStatusProd.Production);

LINQ Using Max() to select a single row

I'm using LINQ on an IQueryable returned from NHibernate and I need to select the row with the maximum value(s) in a couple of fields.
I've simplified the bit that I'm sticking on. I need to select the one row from my table with the maximum value in one field.
var table = new Table { new Row(id: 1, status: 10), new Row(id: 2, status: 20) }
from u in table
group u by 1 into g
where u.Status == g.Max(u => u.Status)
select u
This is incorrect but I can't work out the right form.
BTW, what I'm actually trying to achieve is approximately this:
var clientAddress = this.repository.GetAll()
.GroupBy(a => a)
.SelectMany(
g =>
g.Where(
a =>
a.Reference == clientReference &&
a.Status == ClientStatus.Live &&
a.AddressReference == g.Max(x => x.AddressReference) &&
a.StartDate == g.Max(x => x.StartDate)))
.SingleOrDefault();
I started with the above lambda but I've been using LINQPad to try and work out the syntax for selecting the Max().
UPDATE
Removing the GroupBy was key.
var all = this.repository.GetAll();
var address = all
.Where(
a =>
a.Reference == clientReference &&
a.Status == ClientStatus.Live &&
a.StartDate == all.Max(x => x.StartDate) &&
a.AddressReference == all.Max(x => x.AddressReference))
.SingleOrDefault();
I don't see why you are grouping here.
Try this:
var maxValue = table.Max(x => x.Status)
var result = table.First(x => x.Status == maxValue);
An alternate approach that would iterate table only once would be this:
var result = table.OrderByDescending(x => x.Status).First();
This is helpful if table is an IEnumerable<T> that is not present in memory or that is calculated on the fly.
You can also do:
(from u in table
orderby u.Status descending
select u).Take(1);
You can group by status and select a row from the largest group:
table.GroupBy(r => r.Status).OrderByDescending(g => g.Key).First().First();
The first First() gets the first group (the set of rows with the largest status); the second First() gets the first row in that group.
If the status is always unqiue, you can replace the second First() with Single().
Addressing the first question, if you need to take several rows grouped by certain criteria with the other column with max value you can do something like this:
var query =
from u1 in table
join u2 in (
from u in table
group u by u.GroupId into g
select new { GroupId = g.Key, MaxStatus = g.Max(x => x.Status) }
) on new { u1.GroupId, u1.Status } equals new { u2.GroupId, Status = u2.MaxStatus}
select u1;
What about using Aggregate?
It's better than
Select max
Select by max value
since it only scans the array once.
var maxRow = table.Aggregate(
(a, b) => a.Status > b.Status ? a : b // whatever you need to compare
);
More one example:
Follow:
qryAux = (from q in qryAux where
q.OrdSeq == (from pp in Sessao.Query<NameTable>() where pp.FieldPk
== q.FieldPk select pp.OrdSeq).Max() select q);
Equals:
select t.* from nametable t where t.OrdSeq =
(select max(t2.OrdSeq) from nametable t2 where t2.FieldPk= t.FieldPk)
Simply in one line:
var result = table.First(x => x.Status == table.Max(y => y.Status));
Notice that there are two action.
the inner action is for finding the max value,
the outer action is for get the desired object.

Categories