Added conditions to LinQ-query are not included - c#

Normally it should work and i saw on forum questions like this but for me it is very strange and the where conditions are not addes although they are executed.
Here is my code:
var query = context.infoes.Join(context.users, u => u.Id, a => a.Id, (u, a) => new { U = u, A = a }).
Where(j=>j.A.IA==true).
Where(j=>j.U.G==(int)model.G).
Where(j=>j.U.CI==model.CI);
if (MA != null)
{
query.Where(j => j.U.BD < MD);
}
if (MIA != null)
{
query.Where(j => j.U.BD > MIA);
}
query.Where(j => j.U.Id == 10);
int countResults = query.Count();
This last where i've just added to be sure, but it is not added either. So after the first line of code in intermidiate window i put query and i saw the sql, so i've let the code run and add the other where, but the query didn't change it still defined only the base conditions.
Am i doing something wrong?

Just assign the conditions back to query, like
query = query.Where(j => j.U.BD < MD);

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

Null expression with dynamic LINQ

I have a method with the following signatures that return a C# expression
Expression<Func<T, bool>> GetExpression<T>(IList<Filter> filters)
Then the following code that uses Dynamic LINQ
using (TestContext tEntities = new TestContext())
{
var filterExp = Exp.ExpressionBuilder.GetExpression<Client>(filters);
var filteredCollection = tEntities.Client.Where(filterExp);
IQueryable<Client> queryResult;
if (filterExp == null)
queryResult = tEntities.Client;
else
queryResult = tEntities.Client.Where(filterExp);
}
This is a simple scenario.
I have queries that are 50 lines long sometimes more. I want to avoid having the same code twice with only difference using the where clause.
Does anyone knows if I achieve something different?
from product in context.Product.Where(deleg)
.Include(x => x.Type)
.Include(x => x.Category)
.Include(x => x.WareHouse)
.Include(x => x.Photos)
join f in context.Favorite on product.Id equals f.ProductFid into fg
from fgi in fg.Where(f => f.UserFid == userId).DefaultIfEmpty()
orderby product.Id descending
select new ProductngDto()
{
ProductItem = product,
FavoriteId = fgi != null ? fgi.Id : (long?)null
}).Skip(page * pageSize).Take(pageSize);
Thanks in advance
One of the nice things about IQueryable<T> and IEnumerable<T> is the fact that they are so abstract, you can easily chain them together. One solution could be to structure your code like this:
using (TestContext tEntities = new TestContext())
{
var filterExp = Exp.ExpressionBuilder.GetExpression<Client>(filters);
var filteredCollection = tEntities.Client.Where(filterExp);
IQueryable<Client> queryResult = tEntities.Client;
if (filterExp != null)
{
queryResult = queryResult.Where(filterExp);
}
//do something else with queryResult
}
This way you can continue using queryResult without having to know, or even care, about whether or not filterExp was applied.
For the second example, shuffling it around could look something like this:
var query = from p in context.Product
.Include(x => x.Type)
.Include(x => x.Category)
.Include(x => x.WareHouse)
.Include(x => x.Photos);
if (deleg != null)
{
query = query.Where(deleg);
}
query = from product in query
join f in context.Favorite on product.Id equals f.ProductFid into fg
from fgi in fg.Where(f => f.UserFid == userId).DefaultIfEmpty();
orderby product.Id descending
select new ProductngDto()
{
ProductItem = product,
FavoriteId = fgi != null ? fgi.Id : (long?)null
}).Skip(page * pageSize).Take(pageSize);
Another option you have, is to check the filterExp for null and assign an "always true" lambda to it.
If you control the GetExpression<T> method, you could add it in there as a last step. If you don't control it, you could do the null checking in the methods where you use it's result.
This will allow you to keep your code looking a bit cleaner, but the trade-off is a small performance hit for having to evaluate the lambda all the time.
Depending on how your expressions are typed, this might be as easy as:
if (filterExp == null)
{
filterExp = (_) => true;
}

OUTER APPLY is not supported in this LINQ query

I need to run this query in a oracle 11.2 database, but the generated query contains a "OUTER APPLY". How can I solve it ?
var query = from r in Ctx.Reg
let status_1 = (r.Hist.OrderByDescending(o => o.Id).FirstOrDefault(h => h.RegId == r.Id).Status == 1)
let status_2 = (r.Hist.OrderByDescending(o => o.Id).Skip(1).FirstOrDefault(h => h.RegId == r.Id).Status == 2)
select new
{
r.Id,
...
status_1,
status_2
};
OUTER APPLY is generated by your let statement expressions. You can avoid it by turning them into EXISTS translatable expressions using the equivalent Any based LINQ constructs:
var query = from r in Ctx.Reg
let status_1 = r.Hist.OrderByDescending(h => h.Id).Take(1).Any(h => h.Status == 1)
let status_2 = r.Hist.OrderByDescending(h => h.Id).Skip(1).Take(1).Any(h => h.Status == 2)
select new
{
r.Id,
...
status_1,
status_2
};
Note that h.RegId == r.Id is not needed because that is implied by r.Hist navigation property.

having trouble with groupby and sum

Good evening guys. I'm currently developing a web application with the use of asp.net mvc and C# and I'm having trouble with using the groupby() and sum(). I'm attaching a screenshot of the output to better understand the situation i'm in.
what I want to do here is to group all with the same description and the total is to be summed. I have tried groupby and sum but i cant seem to arrive at the desired output. Can anyone please give me some good points on how to do this?
here is the codes i have tried in the manager:
public IEnumerable<TuitionFeeRevenuePerProgramVM_L> getDetails(string orgUnit, short? academicYear, byte? academicPeriod)
{
var depts = getAllOrgUnitDepByOrgUnitSchID(orgUnit);
var revenue = tuitionFee.SearchFor(x => x.AcademicPeriod == academicPeriod && x.AcademicYear == academicYear && depts.Contains(x.Course.OrgUnitCode) && x.IsLatest == true, "AssessmentItem.ChartOfAccount,AssessmentItem.ChartOfAccount.CostCenter,Course,Course.CourseSchedule,Course.OrgUnit,Course.OrgUnit.OrgUnitHierarchy1,Course.OrgUnit.OrgUnitHierarchy1.OrgUnit").ToList();
var r = revenue.GroupBy(x => x.Course.OrgUnitCode).Select(x => x.First()).ToList(); //this line seems to group them but the output is the first total amount not the overall total
var rev = revenue.Select(x => new TuitionFeeRevenuePerProgramVM_L
{
CostCenterCode = x.AssessmentItem.ChartOfAccount.CostCenterID,
CostCenterDesc = x.Course.OrgUnit.OrgUnitDesc,
Subjects = getNumberOfSubjects(x.Course.OrgUnitCode, x.AcademicYear, x.AcademicPeriod),
Enrolees = revenue.Where(y => y.Course.OrgUnitCode == x.Course.OrgUnitCode).Select(z => z.Course.CourseSchedule.Sum(a => a.EnrolledStudents)).Sum(b => b.Value),//(int)x.Course.CourseSchedule.Sum(y => y.EnrolledStudents),
Total = (decimal)((x.Course.CourseSchedule.Sum(y => y.EnrolledStudents) * x.Amount) * x.Course.ContactHrs) /*(revenue.Where(y => y.Course.OrgUnitCode == x.Course.OrgUnitCode).Select(z => z.Course.CourseSchedule.Sum(a => a.EnrolledStudents)).Sum(b => b.Value) * revenue.Where(d => d.Course.OrgUnitCode == x.Course.OrgUnitCode).Sum(e=>e.Amount)) * revenue.Where(f => f.Course.OrgUnitCode == x.Course.OrgUnitCode).Sum(g=>g.Course.ContactHrs) //*/
});
return rev.ToList();
}
the controller,model and view is as is since all the logic involved will be placed in the manager. I have tried to use Sum() when returning but it gets an error since the controller receives a list and not a decimal.
from d in data
group d by d.Description into g
from gg in g
select new {Description = gg.Key, Sum = gg.Sum(ggg=>ggg.TotalCost)}
Something like that without seeing the data class

Execute IQueryable after it's being done building

I want to build filters on IQueryable depending on user input and execute the query only at the end. I'm trying to understand concept behind and if this is going to work as expected.
Will the query below hit database on return fetchedWorkflowLogs.ToList() in the example below?
// Partition latest record for Instance
IQueryable<WorkflowLog> fetchedWorkflowLogs
= this._workflowLog_repo
.GetAll()
.GroupBy(log => log.ID)
.Select(
grp => new
{
grp = grp,
MaxID = grp.Max(log => log.ID)
}
)
.SelectMany(
temp0 => temp0.grp,
(temp0, log) => new
{
temp0 = temp0,
log = log
}
)
.Where(temp1 => (temp1.log.ID == temp1.temp0.MaxID))
.Select(temp1 => temp1.log);
// .. some more filters
// Last filter
// Filter by Project
if (model.ProjectID != null)
{
fetchedWorkflowLogs.Where(record => record.Project.ID == model.ProjectID);
}
return fetchedWorkflowLogs.ToList();
Agree with David. if GetAll() returns IQueryable, and you return fetchedWorkflowLogs.ToList() will result in the linq query being evaluated, and the query will hit database.
It's the best if you can debug and step through, and writing a test could help you if execuing program directly is difficult.

Categories