I have the following Linq-to-SQL statement:
return db.Photos.SingleOrDefault(p => p.PhotoID == id
&& includePending ? true : p.Live);
For includePending I am passing false. In the database, "Live" is true for all but 2 photos.
However instead of returning one photo as expected, it returns ALL photos in the database except for 2! PhotoID is a primary key (thus can only be true for one item) and boolean logic states that FALSE AND TRUE = FALSE. So what's going on here? Why is it ignoring the p.PhotoID == id portion of my query?
I don't remember the full precedence rules by heart, but your condition is equivalent to:
p => (p.PhotoID == id && includePending) ? true : p.Live
whereas you want:
p => p.PhotoID == id && (includePending ? true : p.Live)
Just use the latter form to make it explicit, or even change it to not use the conditional:
p => p.PhotoID == id && (includePending || p.Live)
which I'd argue is simpler. I would suggest that in situations like this you use bracketing to make the logic clearer even when the precedence rules work in your favour.
You could even use two where clauses:
.Where(p => p.PhotoID == id)
.Where(p => includePending || p.live)
or even conditionalise the second:
var query = ...
.Where(p => p.PhotoID == id);
if (!includePending)
{
query = query.Where(p => p.live);
}
Related
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
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)
I have a statement that reads
ID == repository.Where(x => x.Value == "1" || x.Value == "2")
.Select( x => x.Id).FirstOrDefault();
This will result in 2 IDs, lets say, 1 and 2, therefore using FirstOrDefault() is incorrect as ID may equal the other value which isn't first.
Using linq(and preferably not a foreach loop) how can I say if the ID equals any of the results that come from the linq query?
EDIT - No one seems to understand what I am asking. Therefore I will explain a bit of what the above is doing and say why this is causing my problem, and then how the answer I marked below helps me before it is closed.
Basically, the FirstOrDefault() will return ONE value from one of the where clause values. (Which is the desired affect) - However, as there is an OR condition, it will bring back twos IDs which means there is a 50/50 chance that the outer condition (the one what I say ID == linq query) could be true.
So the solution is too remove the FirstOrDefault() as remember, this returns ONE value and replace it with Any() which basically means if my ID matches ANY of the returned IDs from the linq query result, then the outer condition is true. Please look at my answer.
Once you have your sequence:
repository
.Where(x => x.Value == "1" || x.Value == "2")
.Select( x => x.Id)
You can use .Any() on that sequence to determine if any item therein matches a given condition:
repository
.Where(x => x.Value == "1" || x.Value == "2")
.Select( x => x.Id)
.Any(x => x == ID)
You can use Contains method
repository
.Where(x => x.Value == "1" || x.Value == "2")
.Select(x =>x.Id)
.Contains(ID)
How can I check for more than one condition in the where clause of LINQ to Entities?
How can I check if the value is false or null
.Where(p => (p.Disabled == false || p.Disabled = null));
You can combine conditions using the usual Boolean operators.
Your solution is missing == in the second part of the condition:
.Where(p => (p.Disabled == false || p.Disabled == null));
// Here --------------------^
You can simplify this further, because checking for a nullable bool to be false or null is equivalent to checking for it not being true:
.Where(p => p.Disabled != true);
I have a List that contains Supplier data and I would like to search it by using SupplierID, non-active supplier and only 1 latest result.
So I've got:
List<Supplier> filteredList = this.toList();
filteredList.OrderByDescending(m => m.ModifiedDatetime).FirstOrDefault();
filteredList.Where(f => (f.Active == false && f.FieldId == SupplierFieldID))
.ToList<Supplier>();
But I can't make this work; please help.
You need to chain your LINQ expressions, like this:
var filteredList = unfilteredData
.Where(f => f.Active == false && f.FieldId == SupplierFieldID)
.OrderByDescending(m => m.ModifiedDatetime)
.FirstOrDefault();
You do not need a ToList(), because you need a single item, not a list; this is what FirstOrDefault() does. If you need the last item, you need to order by the reverse of your original ordering condition. For example, if you would like the entry with the latest modified date, you need to order by descending (as you did).
You can do this in one statement, chaining together the LINQ operators:
var filteredList = myList.Where(f => f.Active == false && f.FieldId == SupplierFieldID)
.OrderByDescending(m => m.ModifiedDatetime)
.Take(1);
or as #Preston Guillot suggested, the even shorter form:
var filteredList = unfilteredData
.OrderByDescending(m => m.ModifiedDatetime)
.FirstOrDefault(f => f.Active == false && f.FieldId == SupplierFieldID);