Not sure if this possible using the LINQ method chain syntax or at all, but I would like to conditionally insert a where class in the chain if a parameter passed to the method is not null.
Here is the redundant code I would like to simplify:
public ICollection<Organization> getNetworkServiceRecipients(string serviceId = null)
{
ICollection<Organization> children = get_all_children();
if (serviceId != null)
{
return children.SelectMany(o => o.receives_these_services)
.Where(s => s.serviceId == serviceId)
.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
}
else
{
return (children.SelectMany(o => o.receives_these_services)
.Select(o => o.serviceRecipient)
.Distinct()
.ToList());
}
}
I have been trying to insert the where clause programmatically based on whether serviceId is null or not. All of the answers I have found where based on the query syntax, but I couldn't translate. Any suggestions?
If you don't want to have it in the actual where query as dotnetom mentioned, you can do something like this:
public ICollection<Organization> getNetworkServiceRecipients(string serviceId = null)
{
var services = get_all_children().SelectMany(o => o.receives_these_services);
if (serviceId != null)
services = services.Where(s => s.serviceId == serviceId);
return services.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
}
You can try this approach:
public ICollection<Organization> getNetworkServiceRecipients(string serviceId = null)
{
ICollection<Organization> children = get_all_children();
return children.SelectMany(o => o.receives_these_services)
.Where(s => serviceId == null || s.serviceId == serviceId)
.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
}
In this case if your variable serviceId is null then only the first part of where condition would be executed, otherwise first part would be true and only second condition would matter.
Similar answer as dotnetom, but uses a ternary to determine which lambda to use
so that serviceId == null doesn't get executed on a per-item basis.
return children.SelectMany(o => o.receives_these_services)
.Where( serviceId == null ? (_ => true) : (s => s.serviceId == serviceId))
.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
Related
I have used this to pick just a single column from the collection but it doesn't and throws casting error.
ClientsDAL ClientsDAL = new DAL.ClientsDAL();
var clientsCollection= ClientsDAL.GetClientsCollection();
var projectNum = clientsCollection.Where(p => p.ID == edit.Clients_ID).Select(p => p.ProjectNo).ToString();
Method:
public IEnumerable<Clients> GetClientsCollection(string name = "")
{
IEnumerable<Clients> ClientsCollection;
var query = uow.ClientsRepository.GetQueryable().AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(x => x.Name.Contains(name));
}
ClientsCollection = (IEnumerable<Clients>)query;
return ClientsCollection;
}
As DevilSuichiro said in comments you should not cast to IEnumerable<T> just call .AsEnumerable() it will keep laziness.
But in your case it looks like you do not need that at all because First or FirstOrDefault work with IQueryable too.
To get a single field use this code
clientsCollection
.Where(p => p.ID == edit.Clients_ID)
.Select(p => p.ProjectNo)
.First() // if you sure that at least one item exists
Or (more safe)
var projectNum = clientsCollection
.Where(p => p.ID == edit.Clients_ID)
.Select(p => (int?)p.ProjectNo)
.FirstOrDefault();
if (projectNum != null)
{
// you find that number
}
else
{
// there is no item with such edit.Clients_ID
}
Or even simpler with null propagation
var projectNum = clientsCollection
.FirstOrDefault(p => p.ID == edit.Clients_ID)?.ProjectNo;
I want to display results that don't have a secondary ID displayed first and then display items that do have a secondary ID. But then I need to Skip and Take.
IQueryable<thing> result;
IQueryable<thing> result2;
result2 = result
.Where(t => !(t.second_id == null || t.second_id.Trim() == string.Empty))
.OrderBy(t => t.second_id);
result = result
.Where(t => (t.second_id== null || t.second_id.Trim() == string.Empty))
.OrderBy(t => t.first_id);
result = result.Concat(result2);
return result
.Select(t => t.primary_key)
.Skip(pageSize * pageNumber)
.Take(pageSize)
.ToList();
The problem is that after Concat the IQueryable is no longer technically ordered so Skip and Take throw an error. Like this:
PagedList error: The method 'OrderBy' must be called before the method 'Skip'
You can do it in one query with the conditional operator:
return result.OrderBy(t => (t.second_id != null && t.second_id.Trim() != String.Empty))
.ThenBy(t => (t.second_id != null && t.second_id.Trim() != String.Empty) ? t.second_id : t.first_id)
.Select(t => t.primary_key)
.Skip(pageSize * pageNumber)
.Take(pageSize)
.ToList();
It would need some adjustment if you need to order duplicate second_id in some way, but your original code doesn't.
PS I folded in the negation operator since I think it reads more clearly.
You can do the job with a single query
result = result
.OrderByDescending(t => (t.second_id== null || t.second_id.Trim() ==
string.Empty))
.ThenBy(t => t.second_id)
.ThenBy(t => t.first_id)
.Select(t => t.primary_key)
.Skip(pageSize * pageNumber)
.Take(pageSize)
.ToList();
This question already has answers here:
Linq to SQL multiple conditional where clauses
(3 answers)
Closed 7 years ago.
I want to have multiple where clauses in linq but out of them only one should execute, i was trying something like this:
public JsonResult GetPost(int? id, int? tagid, DateTime? date)
{
var ret = from data in db.Posts.Include(x => x.Tags)
.Include(x => x.Neighbourhood)
.Where((x => x.NeighbourhoodId == id) || (y => y.PostedDate == date) || third condition).ToList()
but i was unable to put second and third condition there becoz after putting dot after y, i cant see any options.
Now, out of these three, only one parameter would have value and other two would have null so, it should checks for parameter only with value.
should i write query like this, is it correct way:
if (id != null)
{
//whole query here
}
else if (tagid != null)
{
//whole query here
}
else (date != null)
{
//whole query here
}
Is it the best way to do this or something else is possible. many many thnks in advance for any suggestion.
Something like this?
var ret = from data in db.Posts.Include(x => x.Tags)
.Include(x => x.Neighbourhood)
.Where(x => x.NeighbourhoodId == (id ?? x.NeighbourhoodId) &&
x.<condition> == (tagid ?? x.<condition>) &&
x.PostedDate == (date ?? x.PostedDate).ToList();
Or like this:
var ret = from data in db.Posts.Include(x => x.Tags)
.Include(x => x.Neighbourhood)
.Where(x => id.HasValue ? x.NeighbourhoodId == id :
tagid.HasValue ? x.<condition> == tagid :
x.PostedDate == date).ToList();
Another option is to build your query more dynamically. I think this also makes your code more human readable, and your conditions can be more complex if needed (for example, build your query inside a loop or something). And you can use this with any other operator, like Include etc. Once your query is built, you can call ToList().
var ret = db.Posts.Include(x => x.Tags).Include(x => x.Neighbourhood);
if (id != null)
{
ret = ret.Where((x => x.NeighbourhoodId == id);
}
else
{
...
}
var result = ret.ToList();
You could use the following:
var ret = from data in db.Posts.Include(x => x.Tags)
.Include(x => x.Neighbourhood)
.Where(x => id == null || x.NeighbourhoodId == id)
.Where(x => date == null || y.PostedDate == date)
.ToList();
If the paramter is null, the where-clauses returns every element of the sequence. If its not null it only returns the elements which matches.
I've tried some combinations but I just don't understand how to do the following:
Lets say I have tables Requests and RequestActivities. I need to get all request sorted by RequestActivity.TimeOfCreation in descending order but RequestActivity may be null.
List<DA.GeneralRequest> ongoingGeneralRequests = db.GeneralRequests
.Where(t => t.GeneralRequestStatusID != 3 && (t.SupervisorID == currentUserId || t.CreatorID == currentUserId || t.AssignedUsers.Any(au => au.UserID == currentUserId)))
.OrderByDescending(x => x.GeneralRequestActivities.OrderBy(ga => ga.GeneralRequestActivityDate).Last().GeneralRequestActivityDate) //gives exeption
.ThenBy(a => a.Deadline).ToList();
I'm not really familiar with LINQ-To-SQL but doesn't work MAX in this case?
.OrderByDescending(x => x.GeneralRequestActivities
.Max(ga => ga.GeneralRequestActivityDate))
.ThenBy(a => a.Deadline)
.ToList();
You need to first cache the ordering value, and then order by the date if it is not null, else by some default date you want:
List<DA.GeneralRequest> ongoingGeneralRequests = db.GeneralRequests
.Where(t => t.GeneralRequestStatusID != 3 && (t.SupervisorID == currentUserId || t.CreatorID == currentUserId || t.AssignedUsers.Any(au => au.UserID == currentUserId)))
.Select(x => new {
Value = x,
OrderByValue = x.GeneralRequestActivities
.OrderBy(ga => ga.GeneralRequestActivityDate)
.LastOrDefault()) // cache value
.OrderByDescending(x => x.OrderByValue != null ?
OrderByValue.GeneralRequestActivityDate
: some default value)
.ThenBy(a => a.Value.Deadline)
.Select(a => a.Value)
.ToList();
Note that you can't use Last() extension method on empty IEnumerable. This is why you get the exception:
InvalidOperationException : The source sequence is empty.
In this line:
x.GeneralRequestActivities.OrderBy(ga => ga.GeneralRequestActivityDate).Last()
x.GeneralRequestActivities is empty, so calling Last() on it result on the exception.
Instead, use the LastOrDefault() extension method, which return null if the IEnumerable is empty.
Return Value Type: TSource default (TSource) if the source sequence is
empty; otherwise, the last element in the IEnumerable.
I am trying to order a list of products based on the zindex property of the cross reference table with the category table (in this case called 'Chassis'), but I get the following error:
Cannot order by type 'System.Collections.Generic.IEnumerable`1[System.Int32]'.
The following is the method I am using:
public IQueryable<E_Product> Product_GetList_ByChassisId(int chassisId)
{
return dc.E_Products
.Where(x => x.Deleted == false)
.Where(x => x.Published == true)
.Where(x => x.E_Product_Chassis
.Any(c => c.ChassisId == chassisId && c.Deleted == false))
.OrderBy(x => x.E_Product_Chassis.Select(c => c.Zindex));
}
I understand the .Select method returns an IEnumerable, but being a many-to-many relationship, x.E_Product_Chassis does not allow simple selection of its properties (e.g. x.E_Product_Chassis.Zindex).
Any help would be very appreciated...
FirstOrDefault(), Min(), Max() -- use one of these functions to select the appropriate z-index out of the set.
public IQueryable<E_Product> Product_GetList_ByChassisId(int chassisId)
{
return dc.E_Products
.Where(x => x.Deleted == false)
.Where(x => x.Published == true)
.Where(x => x.E_Product_Chassis
.Any(c => c.ChassisId == chassisId && c.Deleted == false))
.OrderBy(x => x.E_Product_Chassis.Min(c => c.Zindex));
}