Linq query where Contains() checks for list of strings - c#

Consider the Following Query:
var query = from o in this.OrderManager.LoadOrders()
join s in this.SkuManager.LoadSkus() on o.SKU equals s.SKU
where o.ORDER_ID == orderId
let parcelItem = o.RPU != "Y" && o.DROP_SHIP != "Y" && s.TRUCK_SHIP != "T" && o.SKU != "ABC-123" && o.SKU != "XYZ-789" && o.SKU != "JKL-456"
select new OrderMailLineItem
{
OrderId = o.ORDER_ID,
Sku = s.SKU,
WarehouseId = s.SITE_ID,
QualifyingItem = qualifyingItem,
OversizedItem = parcelItem && (s.DROP_SHIP == null)
};
I would like to be able to write the line let parcelItem = ... to be more like !o.SKU.Contains(skuList) where:
List<string> skuList = new List<string> { "ABC-123", "XYZ-789", "JKL-456"};

You should check whether SKU is not in list instead of checking whether list is in SKU:
let parcelItem = !skuList.Contains(o.SKU)

!skuList.Contains(o.SKU) is exactly how you'd usually do it.
But you could write an In operator, if you like:
public static class ExtensionMethods
{
public static bool In<T>(this T t, params T[] values)
=> values.Contains(t);
}
...
let parcelItem = o.RPU != "Y" && o.DROP_SHIP != "Y" && s.TRUCK_SHIP != "T" &&
!o.SKU.In("ABC-123", "XYZ-789", "JKL-456")
I doubt that'll work with Linq to SQL though.

I don't see why this wouldn't work. You would just need to check those three Yes/No flags in addition to your SKU list.
var skuList = new[] { "ABC-123", "XYZ-789", "JKL-456"};
var query = from o in this.OrderManager.LoadOrders()
join s in this.SkuManager.LoadSkus() on o.SKU equals s.SKU
where o.ORDER_ID == orderId
let parcelItem = o.RPU != "Y" && o.DROP_SHIP != "Y" && s.TRUCK_SHIP != "T" && skuList.Contains(o.SKU)
select new OrderMailLineItem
{
OrderId = o.ORDER_ID,
Sku = s.SKU,
WarehouseId = s.SITE_ID,
QualifyingItem = qualifyingItem,
OversizedItem = parcelItem && (s.DROP_SHIP == null)
};

Related

Linq "where" condition with nullable object property result in "invoke non static method requires a target"

I've tried to find a similar question but I haven't found exactly what I want.
I have this issue: about the following code I am not able to find a way to manage that if devTab is null then his property ID is not available and the where condition t.IDDevTab ==devTab.ID leads to null reference error. Mind that t.IDDevTab is not nullable in the DB. I have tried to insert some additional conditions to the Where but it resulted in error: invoke non static method requires a target. I would obtain a empty list in "DeviceTabColumnsNameAndDesc" if "devTab" is null!
DeviceTable devTab = (from t in _db.DeviceTables
where t.DeviceType == devtype && t.IDPlant == id
select t)
.FirstOrDefault();
var DeviceTabColumnsNameAndDesc = (from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID
&& t.MeasureFamily != "KEY"
&& t.MeasureFamily != "DATETIME"
select new
{
colName = t.ColumnName,
colDescr = t.ColumnDescr
})
.ToList();
Is there a workaround of this problem? Thank you in advance.
So, if devTab is null you want a new empty list of anonymous type.. Awkward, but doable:
DeviceTable devTab = (from t in _db.DeviceTables
where t.DeviceType == devtype && t.IDPlant == id
select t)
.FirstOrDefault();
var DeviceTabColumnsNameAndDesc = devTab == null ?
Enumerable.Empty<object>().Select(x => new { colName = "", colDescr = "" }).ToList() :
(
from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID && t.MeasureFamily != "KEY" && t.MeasureFamily != "DATETIME"
select new {
colName = t.ColumnName,
colDescr = t.ColumnDescr
}
).ToList();
If you wanted to switch away from anonymous types in this case, you could look at ValueTuple:
var DeviceTabColumnsNameAndDesc = devTab == null ?
new List<(string colName, string colDesc)>() :
(
from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID && t.MeasureFamily != "KEY" && t.MeasureFamily != "DATETIME"
select (
colName: t.ColumnName,
colDescr: t.ColumnDescr
)
).ToList();
Or make a record, which is like a class but with some extra bits that make it useful as a data holder:
//defined in a namespace
public record ColThing(string ColName, string ColDesc);
var DeviceTabColumnsNameAndDesc = devTab == null ?
new List<ColThing>() :
(
from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID && t.MeasureFamily != "KEY" && t.MeasureFamily != "DATETIME"
select new ColThing(t.ColumnName, t.ColumnDescr)
).ToList();

LINQ with Contains return zero rows if i am passing empty string

i don't know how to handle this in LINQ
simply i have a searchKey in which i am passing user enter data and it return with rows. but if i am not passing any searchkey it not given any data. i dont want to add contains if searchkey is empty :(
var AppointmentList = (from app in Con.ios_Appointment
where (app.IS_DELETED == false && app.CLINICIANID == appReq.id
&& app.FNAME.Contains(appReq.searchKey.Trim()) || app.LNAME.Contains(appReq.searchKey.Trim()) || app.ADDRESS.Contains(appReq.searchKey.Trim())
)
orderby app.DATE descending
select new
{
app.ID,
app.FNAME,
app.LNAME,
app.DATE,
app.LONGITUDE,
app.LATITUDE,
app.ADDRESS,
app.STATUS,
app.START_TIME
}).Skip(skipRecord).Take(Convert.ToInt32(record)).ToList();
I suggest you use method syntax to easily build the query up programatically:
var query = Con.ios_Appointment.Where(app => !app.IS_DELETED && app.CLINICIANID == appReq.id);
var search = appReq.searchKey.Trim();
if (search != "")
{
query = query.Where(app => app.FNAME.Contains(search) ||
app.LNAME.Contains(search) ||
app.ADDRESS.Contains(search));
}
var appointments = query
.OrderByDescending(app => app.DATE)
.Select(app => new
{
app.ID,
app.FNAME,
app.LNAME,
app.DATE,
app.LONGITUDE,
app.LATITUDE,
app.ADDRESS,
app.STATUS,
app.START_TIME
})
.Skip(skipRecord)
.Take(Convert.ToInt32(record))
.ToList();
You need to use string.IsNullOrWhiteSpace method:
where (app.IS_DELETED == false &&
app.CLINICIANID == appReq.id &&
(string.IsNullOrWhiteSpace(appReq.searchKey) ||
app.FNAME.Contains(appReq.searchKey.is Trim()) || ...

How to refactor this linq query?

I have this below linq query of which 95% remains the same across 6 different scenarios.How can I refactor it so that I can write in one function and use it for all the cases?
var results = await (from r in Requests
join ra in RequestAuthorisers
on cr.ID equals ra._REQUEST_ID
where cr.FACILITY_ID == facilityId
&& (r._REQUEST_STATUS_ID == RequestSubmitted || r._REQUEST_STATUS_ID == RequestPartiallyAuthorised )//check against more status codes based on different conditions
&& ra.FACILITY_USER_ID == facilityUserId//don't check this if admin
&& ra.AUTHORIZATION_DATE != null
&& ra.REJECTION_DATE == null
select new
{
FacilityId = r.FACILITY_ID,
VersionId = r.VERSION_ID,
CreatedByTitle = r.CREATED_BY_USER_TITLE,
CreatedByFirstName = r.CREATED_BY_USER_FIRST_NAME,
CreatedByLastName = r.CREATED_BY_USER_LAST_NAME,
RequestDate = r.SUBMITTED_DATE,
RequestId = r.ID,
RequestType = r._TYPE_ID,
Status = r._REQUEST_STATUS_ID
}).ToListAsync();
var RequestResponse = results.Select(
r => new RequestResponseDto
{
FacilityId = r.FacilityId,
VersionId = r.VersionId,
CreatedByTitle = r.CreatedByTitle,
CreatedByFirstName = r.CreatedByFirstName,
CreatedByLastName = r.CreatedByLastName,
RequestDate = Convert.ToDateTime(r.RequestDate).ToString("s"),
RequestType = ((RequestType)r.RequestType).ToString(),
Status = ((RequestStatus)r.Status).ToString()
}).ToList();
As you can the scenarios/queries differ only by if isAdmin and check against more status codes(as commented above), rest of the part becomes same.
I am using LINQ to EF(DB First).
Thanks in advance.
Why not pass a boolean into the method:
public RequestResponseDto MyMethod(bool isAdmin, Status status)
{
...
&&
((status == Status.Status1 && (r._REQUEST_STATUS_ID == RequestSubmitted || r._REQUEST_STATUS_ID == RequestPartiallyAuthorised))
||
(status == Status.Status2 && (r._REQUEST_STATUS_ID == Something))
||
(status == Status.Status3 && (r._REQUEST_STATUS_ID == SomethingElse || r._REQUEST_STATUS_ID == AnotherThing)))
...
&& (isAdmin || ra.FACILITY_USER_ID == facilityUserId)
...
}

asp.net mvc LINQ datetime problem

string NewsFillter = string.Empty;
List<string> PublishDatePostMeta = (from post in postrepository.GetAllPosts()
join pstmt in postrepository.GetAllPostMetas()
on post.int_PostId equals pstmt.int_PostId
where (post.int_PostTypeId == 4 && post.int_PostStatusId == 2 && post.int_OrganizationId == layoutrep.GetSidebarDetailById(SidebarDetailsId).int_OrganizationId) && pstmt.vcr_MetaKey=="Publish Date"
select pstmt.vcr_MetaValue).ToList();
int DatesCount = PublishDatePostMeta.Count();
foreach (string PublishDate in PublishDatePostMeta)
{
if (PublishDate != "")
{
NewsFillter += System.DateTime.Now + ">=" + Convert.ToDateTime(PublishDate);
}
}
var postsidebar = from post in postrepository.GetAllPosts()
join pstmt in postrepository.GetAllPostMetas()
on post.int_PostId equals pstmt.int_PostId
where (post.int_PostTypeId == 4 && post.int_PostStatusId == 2 && post.int_OrganizationId == layoutrep.GetSidebarDetailById(SidebarDetailsId).int_OrganizationId)
&& (pstmt.vcr_MetaKey.Contains(filter) && pstmt.vcr_MetaValue.Contains("true"))
select post;
1st question .The thing is that how NewsFillter would be accomdated in the postsidebar query in the pstmt object after true ( i would be putting it in contains,equals join or what) .
2nd question . is there any way that a chunk (between &&s) return enumerable and i can get away with this. at this moment it is not allowing that
I haven't udnerstood you properly, but if you want to apply multiple filters, here is my solution:
//Book is a table in the database
List<Expression<Func<Book, bool>>> filters = new List<Expression<Func<Book, bool>>>();
IQueryable<Book> query = dc.Books;
filters.Add(b => b.BookId == long.Parse(id));
//apply all filters
foreach (var f in filters)
query = query.Where(f);
Your questions:
This question requires an example of input and output. Try something like this:
|| pstmt.vcr_MetaKey=="Publish Date" && (
pstmt.vcr_MetaValue == "" || DateTime.Parse(pstmt.vcr_MetaValue) < DateTime.Now)
There is method AsEnumerable, if you mean what I think.

Dynamic LINQ Multiple Where Clause

Still really struggling with this and appear to be going round in circles.
I have the following code that is driving me nuts. It should populate a list of items to be used in an autocomplete text box:
public string[] GetAutoComplete(string prefixText, int count)
{
string memberid = HttpContext.Current.Session["MemberID"].ToString();
string locationid = HttpContext.Current.Session["LocationID"].ToString();
string inhouse = HttpContext.Current.Session["Inhouse"].ToString();
string supplier = HttpContext.Current.Session["Supplier"].ToString();
string groupw = HttpContext.Current.Session["Group"].ToString();
string external = HttpContext.Current.Session["External"].ToString();
MyEnts autocomplete = new MyEnts();
var r = from p in autocomplete.tblAutoCompletes
where p.MemberId == memberid && p.LocationId == locationid && p.ACItem.Contains(prefixText)
select p.ACItem;
if (inhouse == "Inhouse")
r = r.Where(p => p == inhouse);
if (supplier == "Supplier")
r = r.Where(p => p == supplier);
if (groupw == "Group")
r = r.Where(p => p == groupw);
if (external == "External")
r = r.Where(p => p == external);
r.OrderBy(p => p);
return r.ToArray();
What I am trying to retrieve with the dynamic where clause along the lines of the following.
Should inhouse = "Inhouse", then the list of items should include the word "Inhouse". If inhouse != "Inhouse", the word "Inhouse" should be excluded from the list.
This same logic should then be applied across the different where clauses i.e. Supplier, Group, External.
I genuinely have tried lots of different methods but I cannot for the life of me get the thing to work and it's frustrating me somewhat.
If anyone can suggest a way of doing this, you will either get a big kiss or a big frosty beer should our paths ever cross.
Not exactly sure about your problem here but if you want to exclude then shouldn't the code be something like
if (inhouse == "Inhouse")
r = r.Where(p => p == inhouse);
else
r = r.Where(p => p != inhouse);
Oh! if you want just exclusion then the code should be something like
if (inhouse != "Inhouse")
r = r.Where(p => p != inhouse);
If the set of values to include/exclude is known at compile-time (as appears to be the case in your example), I think this can be managed with one query:
string memberid = HttpContext.Current.Session["MemberID"].ToString();
string inhouse = HttpContext.Current.Session["Inhouse"].ToString();
string supplier = HttpContext.Current.Session["Supplier"].ToString();
bool includeInHouse = (inhouse == "Inhouse");
bool includeSupplier = (supplier == "Supplier");
MyEnts autocomplete = new MyEnts();
var r = from p in autocomplete.tblAutoCompletes
where (p.MemberId == memberid && p.LocationId == locationid && p.ACItem.Contains(prefixText))
&& (includeInHouse || (p.ACItem != "InHouse"))
&& (includeSupplier || (p.ACItem != "Supplier"))
select p.ACItem;
r.OrderBy(p => p.ACItem);
return r.ToArray();
I've eliminated a couple cases for brevity.
Wouldn't each of your Where clauses just need to contain a Contains criteria and some Not Contains?
if (inhouse == "Inhouse")
r = r.Where(p => p.Contains(inhouse) && !p.Contains("Supplier") && !p.Contains("Group") && !p.Contains("External"));
Sorted.
var r = from p in autocomplete.tblAutoCompletes
where p.MemberId == memberid && p.LocationId == locationid && p.ACItem.Contains(prefixText)
select p.ACItem;
if (inhouse != "Inhouse")
r = r.Where(p => p != "Inhouse");
if (supplier != "Supplier")
r = r.Where(p => p != "Supplier");
if (groupw != "Group")
r = r.Where(p => p != "Group");
if (external != "External")
r = r.Where(p => p != "External");
r = r.OrderBy(p => p);
return r.ToArray();
I had to set the exception in quotation marks as the session vlaue was inappropriate and wouldn't have picked out anything from the list.
Thanks to all those contributing and helping me out.

Categories

Resources