Linq query construction based on empty parameters - c#

I have an object like this:
public class Filters
{
List<string> A { get; set; }
List<string> B { get; set; }
}
I will use it as parameters of a function that will execute a query on a Database.
These itens, A and B, will indicate the "where" filters.
Look at the following code:
//Filters Initialization
Filters TestFilters = new Filters();
TestFilters.A = new List<string>();
TestFilters.B = new List<string>(new string[] {"testfilter1","testfilter2"}); //WHERE B == "testfilter1" OR B == "testfilter2"
//Call of the function that executes SELECT on DB
List<Table> Result = TestFunction(TestFilter);
The function:
public static List<Table> TestFunction(Filters pParameter)
{
ExampleDataContext dc = new ExampleDataContext(Properties.Settings.Default.ExampleConnectionString);
List<Table> SelectResult = new List<Table>();
if (pParameter.A.count != 0 && pParameter.B.count != 0)
SelectResult = (from x in dc.Table
where pParameter.A.Contains(x.A)
where pParameter.B.Contains(x.B)
select x).ToList();
else if (pParameter.A.count == 0 && pParameter.B.count != 0)
{
SelectResult = (from x in dc.Table
where pParameter.B.Contains(x.B)
select x).ToList();
}
else if (pParameter.A.count != 0 && pParameter.B.count == 0)
{
SelectResult = (from x in dc.Table
where pParameter.A.Contains(x.A)
select x).ToList();
}
else if (pParameter.A.count == 0 && pParameter.B.count == 0)
{
SelectResult = (from x in dc.Table
select x).ToList();
}
return SelectResult;
}
Sometimes A or/and B is/are empty, then I am using "IF" structure to handle it.
Maybe I could face a problem where my filter is bigger, more than 2 parameters, and it would be difficult/boring to code.
My question is: It is working, but is there another way to do it, instead of using IF?

Please have a try(chained linq), I just rewrite by hand and did not compile or run:
public static List<Table> TestFunction(Filters pParameter)
{
ExampleDataContext dc = new ExampleDataContext(Properties.Settings.Default.ExampleConnectionString);
var SelectResult = dc.Table;
if (pParameter.A.count != 0)
SelectResult = from x in SelectResult
where pParameter.A.Contains(x.A)
select x;
if (pParameter.B.count != 0)
{
SelectResult = from x in SelectResult
where pParameter.B.Contains(x.B)
select x;
}
return SelectResult.ToList();
}

You can do something like this:
SelectResult = (from x in dc.Table
where pParameter.A.Any() && pParameter.A.Contains(x.A)
where pParameter.B.Any() && pParameter.B.Contains(x.B)
select x).ToList();

The code is straightforward. Just use correct LINQ syntax.
//Parameters class
public Class Parameters
{
public List<string> A {get; set;}
public List<int> B {get; set;}
}
//some function in a controller
public List<SomeResult> GetResult(Parameters pars)
{
var db = new DbContext();
var result = db.SomeResult.Where(s => s.Any(p =>p.SomeString == pars.A
|| p.SomeInt == pars.B))
.ToList();
return result;
}

with IQueryable<Table> (type of dc.Table and dc.Table.Where()), you can chain criteria, and export ToList only in buttom line.
so if yoy change the SelectResult from List<Table> to IQueryable<Table>,
each criteria can be chained to the previous expression, and it is done as though AND:
public static List<Table> TestFunction(Filters pParameter)
{
ExampleDataContext dc = new ExampleDataContext(Properties.Settings.Default.ExampleConnectionString);
//all
IQueryable<Table> SelectResult = dc.Table;
//add conditions
if (pParameter.A.count != 0 )
SelectResult = SelectResult.Where(x => pParameter.A.Contains(x.B));
if (pParameter.B.count != 0)
SelectResult = SelectResult.Where(x => pParameter.A.Contains(x.B));
//export, with one\two\zero conditions
return SelectResult.ToList();
}
If your case is more complex (such as multiple conditions and with OR operators) consider using the wonderful tool PredicateBuilder.

I have tried some of the options, but unsucessfully.
Below you can find the code that worked for me:
List<Table> SelectResult = (from x in dc.Table
where (pParameter.A.Count != 0 ? pParameter.A.Contains(x.A) : true)
where (pParameter.B.Count != 0 ? pParameter.B.Contains(x.B) : true)
select s).ToList();

Related

Remove a range of items from a list without looping

Hi Is there a more ellegant way of doing this must I do the loop is there like a range funciton I could just remove all the items found
Sorry I should have showing how my qry is being inserted.
Btw these are two different entities that I am removing from I hope you get the idea.
var qry = db.AssemblyListItems.AsNoTracking().Where(x =>
x.ProductionPlanID == (long)_currentPlan.ProductionPlan ).ToList();
var hasbeenAssembled = db.CompletedPrinteds.AsNoTracking().Where(x =>
x.ProductionPlanId == item.ProductionPlanID).ToList();
var hasbeenFound = db.CompletedPrinteds.AsNoTracking().Where(x =>
x.ProductionPlanId== item.ProductionPlanID).ToList();
foreach (var subitem in hasbeenAssembled )
{
if(item.ProductionPlanID ==subitem.ProductionPlanId && item.DocumentNo == subitem.DocumentNo && item.DocumentNo == subitem.DocumentNo && item.OutstandingToMake ==0)
{
qry.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanId && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode && item.OutstandingToMake ==0);
}
}
public List<AssemblyListItems> RemoveDespatchedItems(List<AssemblyListItems> AssemblyItems)
{
foreach (AssemblyListItems item in AssemblyItems)
{
using (var db = new LiveEntities())
{
var hasNotBeenDespatched = db.PalletizedItems.Where(w => w.Despatched != "Not Despatched");
foreach (var subitem in hasNotBeenDespatched)
{
AssemblyItems.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanID && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode);
}
}
}
return AssemblyItems;
}
I just need to remove the items from the first query hasNotBeenDespatched from the second query.As could be over 400 items i want it to be efficient as possible.
Edit 2
I am a we bit closer thanks buts its still not removing the items from the removedespatchitems from the assebmittems I do not no why
public List<AssemblyListItems> RemoveDespatchedItems(List<AssemblyListItems> AssemblyItems, Int64 ProductionPlanId)
{
using (var db = newLiveEntities())
{
List<PalletizedItems> removeDespatchItems = db.PalletizedItems.Where(w => w.Despatched != "Not Despatched" && w.ProductionPlanID == ProductionPlanId).ToList();
var itemsDocumentNo = db.PalletizedItems.Select(x => x.ProductionPlanItemID).ToList();
foreach (var subitem in removeDespatchItems) {
AssemblyItems.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanID && itemsDocumentNo.Contains(x.ProductionPlanItemID) && x.ItemCode == subitem.StockCode && x.LineQuantity==x.AllocatedQuantity);
}
}
return AssemblyItems;
}
Not 100% I get exactly how it should be.
However in general you could use join that would result in it being done in the database. Something like this:
var remainingItems = (from ali in db.FUEL_AssemblyListItems
join completed in db.FuelCompletedPrinteds
on new { ali.ProductionPlanID, ali.DocumentNo, ali.ItemCode } equals new { completed.ProductionPlanID, completed.DocumentNo, completed.StockCode }
join dispatched in db.FUEL_PalletizedItems
on new { ali.ProductionPlanID, ali.DocumentNo, ali.ItemCode } equals new { dispatched.ProductionPlanID, dispatched.DocumentNo, dispatched.StockCode }
where (ali.ProductionPlanID == (long) _currentPlan.ProductionPlan
&& ali.DocumentNo == completed.DocumentNo
&& completed.OutstandingToMake == 0
&& dispatched.Despatched != "Not Despatched")
select ali).ToList();
Depending upon the records in the database the join might need to be a outer join which needs a slightly different syntax but hopefully you've got a starting point.

Linq query where Contains() checks for list of strings

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)
};

Create a search method with AND/OR options in Linq and C#

I'm trying to create some methode for searching and filtring data in databese using c# and asp.net mvc 4 (linq)
public ActionResult Search_Names_Using_Location(string b,string d, int c=0,int Id=0)
{
ViewBag.Locations = db.Locations.ToList();
var agentlocation = new AgentLocationViewModel();
agentlocation.agents = new List<Agent>();
agentlocation.agents = (from a in db.Agents
where a.LocationId == Id
&& (a.LocationName == b)
&& (a.age > c )
select a).ToList();
return View(agentlocation);
}
The problem is that user can let some texboxes empty, so the value of Id or a or b can be null so the query will get nothing.
Is their any suggestions to do that (i can go with if else but that's hard if i have 7 or 8 strings)?
You can check for null inside query
public ActionResult Search_Names_Using_Location(string b,string d,
int c=0,int Id=0,)
{
ViewBag.Locations = db.Locations.ToList();
var agentlocation = new AgentLocationViewModel();
agentlocation.agents = new List<Agent>();
var noId = string.IsNullOrWhitespace(Id);
var noB = string.IsNullOrWhitespace(b);
agentlocation.agents = (from a in db.Agents
where (noId || a.LocationId == Id)
&& (noB || a.LocationName == b)
&& (a.age > c )
select a).ToList();
return View(agentlocation);
}
If you have AND conditions only you can use
var query = db.Agents;
if (Id != 0)
{
query = query.Where(x => x.LocationId == Id)
}
if (!string.IsNullOrWhitespace(b))
{
query = query.Where(x => x.LocationName == b)
}
...
var result = query.ToList(); // actual DB call
This will remove useless empty conditions, like WHERE (0 = 0 OR LocationId = 0)
In case of OR conditions and combinations you can take a look at PredicateBuilder
So you can use Or and And predicate combinations like this:
IQueryable<Product> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
}

"Object reference not set to an instance of an object" for List<T>.Add

In my method I have multiple statements that are filtering data and returning and object. Because I want to serve the view with an view model I instantiate a list and then pass it into view...
Simplified example.
List<viewModel> returnedViewModel = new List<viewModel>();
foreach (var item in filteredData)
{
returnedViewModel.Add( // this line is throwing error
new viewModel
{
data = item,
});
}
The issue is that I'm getting "Object reference not set to an instance of an object." for returnedViewModel.Add for one particular filteredData object while other are not causing that issue.
I'm after a possible source of that problem not the solution.
I'm using EF and have a MARS statement in my connectionString. Can that be an issue?
Whole statment:
public ActionResult RenderPartialSearchableEventStacks(string viewType,
string orderColumn,
string sortOrder,
int? stackNumber,
string adminName,
string clientName,
string stackType,
string stackStatus,
int? adminId = null)
{
IEnumerable<blsEventStack> eventStacks = unitOfWork.EventStackRepository.Filter(n => (stackNumber == null) || n.EventStackId == stackNumber,
n => (String.IsNullOrEmpty(adminName)) || n.ManagingAdminName.Contains(adminName),
n => (String.IsNullOrEmpty(clientName)) || n.RelatedClientName.Contains(clientName),
n => (String.IsNullOrEmpty(stackType)) || n.EventStackType.Contains(stackType),
n => (String.IsNullOrEmpty(stackStatus)) || n.EventStackLastEventStatus.Contains(stackStatus));
IEnumerable<blsEventStack> viewTypeRelatedEventStacks = eventStacks;
if (viewType == "allEventStacks")
{
viewTypeRelatedEventStacks = unitOfWork.EventStackRepository.GetAll();
}
if (viewType == "assignedEventStacks")
{
// Needed to filter out results with empty (unassigned) admin name
viewTypeRelatedEventStacks = from x in eventStacks
where x.ManagingAdminName != null
select x;
}
if (viewType == "singleAdminAssignedEventStacks")
{
string singleAdminName;
if (adminId != null)
{
// Gets admin name using adminId
singleAdminName = unitOfWork.AdminRepository.GetById(adminId).AdminName;
}
else
{
MembershipUser admin = Membership.GetUser();
blsAdmin loggedInAdmin = (from x in unitOfWork.AdminRepository.GetAll()
where x.AdminEmail == admin.Email
select x).FirstOrDefault();
singleAdminName = loggedInAdmin.AdminName;
ViewBag.adminView = true;
}
// Gets all eventStacks that belong to a give adminName
viewTypeRelatedEventStacks = from x in eventStacks
where x.ManagingAdminName == singleAdminName
select x;
ViewBag.adminId = adminId;
}
IEnumerable<blsEventStack> returnedEventStacks = viewTypeRelatedEventStacks;
if (orderColumn == "stack")
{
if (sortOrder == "descendingStack")
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackId descending
select x;
sortOrder = null;
}
else
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
select x;
sortOrder = "descendingStack";
}
}
if (orderColumn == "managingAdmin")
{
if (sortOrder == "descendingManagingAdmin")
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.ManagingAdminName descending
select x;
sortOrder = null;
}
else
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.ManagingAdminName
select x;
sortOrder = "descendingManagingAdmin";
}
}
if (orderColumn == "relatedClient")
{
if (sortOrder == "descendingRelatedClient")
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.RelatedClientName descending
select x;
sortOrder = null;
}
else
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.RelatedClientName
select x;
sortOrder = "descendingRelatedClient";
}
}
if (orderColumn == "stackType")
{
if (sortOrder == "descendingStackType")
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackType descending
select x;
sortOrder = null;
}
else
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackType
select x;
sortOrder = "descendingStackType";
}
}
if (orderColumn == "latestEventTime")
{
if (sortOrder == "descendingLatestEventTime")
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackLastEventTime descending
select x;
sortOrder = null;
}
else
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackLastEventTime
select x;
sortOrder = "descendingLatestEventTime";
}
}
if (orderColumn == "latestEventStatus")
{
if (sortOrder == "descendingLatestEventStatus")
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackLastEventStatus descending
select x;
sortOrder = null;
}
else
{
returnedEventStacks = from x in viewTypeRelatedEventStacks
orderby x.EventStackLastEventStatus
select x;
sortOrder = "descendingLatestEventStatus";
}
}
List<ViewModelAllEventStacks> returnedViewModel = new List<ViewModelAllEventStacks>();
foreach (var item in returnedEventStacks)
{
returnedViewModel.Add(
new ViewModelAllEventStacks
{
EventStack = item,
AdminId = item.AdminEventLogs.FirstOrDefault().AdminId,
ClientId = item.ClientEventLogs.FirstOrDefault().ClientId
});
}
ViewBag.stackNumber = stackNumber;
ViewBag.adminName = adminName;
ViewBag.clientName = clientName;
ViewBag.stackType = stackType;
ViewBag.stackStatus = stackStatus;
// ViewBag passing state of existing order
ViewBag.sortOrder = sortOrder;
// ViewBag passing view data (ex. assignedEventStacks, allEventStacks, etc.)
ViewBag.viewType = viewType;
return PartialView("BLS_AllEventStacks", returnedViewModel);
}
There are 4 possible reasons:
Your code in debug isn't what's actually running and is stale.
There is a null exception in the viewModel constructor.
There is a null exception happening in the data property.
As noted in the comments, there is some code you filtered out that was actually setting the list to null.
Based on edited question
returnedViewModel.Add(
new ViewModelAllEventStacks
{
EventStack = item,
AdminId = item.AdminEventLogs.FirstOrDefault().AdminId,
ClientId = item.ClientEventLogs.FirstOrDefault().ClientId
});
if there are no AdminEventLogs or ClientEventLogs this will throw the null exception error. Also possible but unlikely, item is null.
I believe these two lines are the problem. When you use item.AdminEventLogs.FirstOrDefault(), it will be null if item.AdminEventLogs doesn't have any elements, so accessing .AdminId will raise an error. The same thing also applies to item.ClientEventLogs.FirstOrDefault()
AdminId = item.AdminEventLogs.FirstOrDefault().AdminId,
ClientId = item.ClientEventLogs.FirstOrDefault().ClientId
You can use .Any() to check whether item.AdminEventLogs and item.ClientEventLogs have any elements. If so, use the AdminId and ClientId of the first element, otherwise set AdminId and ClientId to 0 (assuming AdminId and ClientId are integers)
AdminId = item.AdminEventLogs.Any() ? item.AdminEventLogs.First().AdminId : 0,
ClientId = item.ClientEventLogs.Any() ? item.ClientEventLogs.First().ClientId : 0

Building the 'where' clause in a Linq query

In this query, I always want the 'normal' type element.
If the _includeX flag is set, I want the 'workspace' type elements, too.
Is there a way to write this as one query? Or build the where clause based on _includeX before submitting the query?
if (_includeX) {
query = from xElem in doc.Descendants(_xString)
let typeAttributeValue = xElem.Attribute(_typeAttributeName).Value
where typeAttributeValue == _sWorkspace ||
typeAttributeValue == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
}
else {
query = from xElem in doc.Descendants(_xString)
where xElem.Attribute(_typeAttributeName).Value == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
}
You can break it into a separate predicate:
Predicate<string> selector = x=> _includeX
? x == _sWorkspace || x == _sNormal
: x == _sNormal;
query = from xElem in doc.Descendants(_xString)
where selector(xElem.Attribute(_typeAttributeName).Value)
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
Or inline the condition:
query = from xElem in doc.Descendants(_xString)
let typeAttributeValue = xElem.Attribute(_typeAttributeName).Value
where (typeAttributeValue == _sWorkspace && _includeX) ||
typeAttributeValue == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
Or remove query expression usage and do it this way:-
var all = doc.Descendants(_xString);
var query = all.Where( xElem=> {
var typeAttributeValue = xElem.Attribute(_typeAttributeName).Value;
return typeAttributeValue == _sWorkspace && includeX ) || typeAttributeValue == _sNormal;
})
.Select( xElem =>
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
})
Or combine the first and third and do:
Predicate<string> selector = x=> _includeX
? x == _sWorkspace || x == _sNormal
: x == _sNormal;
query = doc.Descendants(_xString)
.Where(xElem => selector(xElem.Attribute(_typeAttributeName).Value))
.Select(xElem => new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};)
It all depends what's going to work cleanest in your context.
Do yourself a favour and buy (and read!) C# in Depth and it'll all make sense a lot more quickly that learning this stuff bit by bit...

Categories

Resources