How to handle Null array in Linq - c#

I have a methods that takes multiple arrays and filter data using linq, the problem I am having is that one or more arrays could be null and I am not sure how to handle null arrays in Linq.:
public IPagedList<SGProduct> GetFilteredProducts(string catID, string[] houseID, int page = 1)
{
return MongoContext.Products.AsQueryable<SGProduct>().AsEnumerable().Where(x =>houseID.Contains(x.HouseID.ToString()) && x.ProductCategoryID.ToString() == catID).ToList().ToPagedList(page, 6);
}
The houseID array could be null also I will have more parameters to this method like Size array etc.
Any suggestions.
Thanks

Just add null check for array, end create empty one if it's null.
public IPagedList<SGProduct> GetFilteredProducts(string catID, string[] houseID, int page = 1)
{
if(houseID == null) houseID = new string[] {};
return MongoContext.Products.AsQueryable<SGProduct>().AsEnumerable().Where(x =>houseID.Contains(x.HouseID.ToString()) && x.ProductCategoryID.ToString() == catID).ToList().ToPagedList(page, 6);
}
Also, you can query variable and build it with conditions as you go. And then you have finished, just materialize and return records.
public IPagedList<SGProduct> GetFilteredProducts(string catID, string[] houseID, int page = 1)
{
var query = MongoContext.Products.AsQueryable<SGProduct>();
if(houseID == null)
query = query.Where(x =>houseID.Contains(x.HouseID.ToString()) && x.ProductCategoryID.ToString() == catID);
return query.ToList().ToPagedList(page, 6);
}

Related

An exception was thrown while attempting to evaluate a LINQ query parameter expression

I got error in the tittle. Probably i must control string.IsNullOrEmpty(searchString) but i didn't it. My code bellow. Please help me
Thank you everyone i solved this problem. Problem was not here. Prooblem in my route codes. for search is different i would write my route codes this
endpoints.MapControllerRoute(
name:"search",
pattern: "{search}",
defaults: new {controller="Shop",action="search"}
);
but not in from of pattern: "{search}
should be this pattern: "search"
Thank you to everyone who helped
public List<Product> GetSearchResult(string searchString)
{
using (var context = new ShopContext())
{
var products = context
.Products
.Where(i=> i.IsApproved && (i.Name.ToLower().Contains(searchString.ToLower()) || i.Description.ToLower().Contains(searchString.ToLower())))
.AsQueryable();
return products.ToList();
}
}
I would break this up as follows:
public List<Product> GetSearchResult(string searchString)
{
// What do you want to do if searchString is null or blank? (Pick one:)
// You could send back an empty result...
if (String.IsNullOrWhiteSpace(searchString))
return null;
// Or you could convert it to a blank string
if (searchString == null)
searchString = "";
List<Product> products = new List<Product>();
using (var context = new ShopContext())
{
products = context.Products.ToList();
}
// Always check for null and empty after going to the DB
if (products == null || products.count = 0)
return null;
// If we are still here, then we can finally do the search
List<Product> results = products.Where(i=> i.IsApproved &&
(i.Name != null && i.Name.ToLower().Contains(searchString.ToLower()) ||
(i.Description != null && i.Description.ToLower().Contains(searchString.ToLower())));
return results;
}
Note: I've not tested this and there may be syntax errors in the last LINQ statement with all of the ('s and )'s.
EDIT:
The example above will pull back ALL records in the Product table and then filter the results in-memory. If you want to avoid that, then I think this should work:
public List<Product> GetSearchResult(string searchString)
{
// What do you want to do if searchString is null or blank? (Pick one:)
// You could send back an empty result...
if (String.IsNullOrWhiteSpace(searchString))
return null;
// Or you could convert it to a blank string
if (searchString == null)
searchString = "";
using (var context = new ShopContext())
{
List<Product> products = context.Products.Where(i=> i.IsApproved &&
(i.Name != null && i.Name.ToLower().Contains(searchString.ToLower()) ||
(i.Description != null && i.Description.ToLower().Contains(searchString.ToLower()))).ToList();
return products;
}
}
The key difference between this and your OP is that we are checking for null's on Name and Description -- and I believe this does it in a way that EF can translate into a query.
Not a lot of information at hand but i assume the LINQ fails because a string is null.
Also why do you create a queryable of your results and then produce a list?
public List<Product> GetSearchResult(string searchString) {
static bool Contains(string a, string b) {
return a.ToLower().Contains(b.ToLower());
}
using (var context = new ShopContext()) {
return context
.Products
.Where(i => i.IsApproved)
.Where(i => i.Name is null ? false : Contains(i.Name, searchString)
|| i.Description is null ? false : Contains(i.Description, searchString))
.ToList();
}
}

How to fix "An expression may not contain a dynamic operation" in Linq?

I have a PUT Request where I passed in a json string value that contains an ID value and the other variables and values. After which I'll deserialize it and find the ID from the json in the Database, and then update the values:
var bookininput = JsonConvert.DeserializeObject<dynamic>(value);
if (bookininput.id == null || bookininput.id == 0)
{
return BadRequest("no id provided");
}
else {
var log = _context.BIBOLogs.Where(input => input.Id == bookininput.id);
}
However, I'm facing issues in .Where(input => input.Id == bookininput.id); because it's telling me that bookininput.id is dynamic and cannot be used for lookup. Any other methods to go around it?
Try using a known class/type when doing your filter instead of dynamic. I'm assuming that the bookininput.id is int on the following example:
var bookininput = JsonConvert.DeserializeObject<dynamic>(value);
if (bookininput.id == null || bookininput.id == 0)
{
return BadRequest("no id provided");
}
else
{
int inputId = int.Parse(bookininput.id);
var log = _context.BIBOLogs
.Where(input => input.Id == inputId);
}

Different group by parameters according to different conditions in c#

I have a class Person having properties as PersonId, PersonAdress, PersonSalary
I have three different cases when I have to apply group by which is
1. When only PersonAdress is null
2. When only PersonSalary is null
3. When both PersonAdress and PersonSalary are null.
PersonId cannot be null.
All these info is in the list List personList.
Can someone help me here?
You have to define an anonymous lambda to define the grouping. But you can also use an existing method.
List<Person> personList = ...;
var agg = personList.GroupBy(Aggregator);
foreach (var gr in agg) {
Console.WriteLine("Group {0} has {1} persons", gr.Key, gr.Count());
}
private static int Aggregator(Person p) {
if (p.PersonAddress == null && p.PersonSalary == null) {
return 3;
} else if (p.PersonSalary == null) {
return 2;
}
return 1;
}

How to compare two lists in .NET

List<Employee> emplist = new List<Employee>();
emplist.Add(new Employee { Name = "Emp_1", BasicSalary = 1000, Id = Guid.NewGuid(), HRA = 100, DA = 10, TotalSalary = 1110 });
emplist.Add(new Employee { Name = "Emp_2", BasicSalary = 1000 * 2, Id = Guid.NewGuid(), HRA = 200, DA = 20, TotalSalary = 2220 });
emplist.Add(new Employee { Name = "Emp_3", BasicSalary = 1000 * 3, Id = Guid.NewGuid(), HRA = 300, DA = 30, TotalSalary = 3330 });
var result = empRep.CallSupportFindAll();
// CollectionAssert.AreEqual(emplist, result);
Assert.AreEqual(emplist, result);
var r1 = result[0];
Assert.AreEqual(r1.Name, emplist[0].Name);
Assert.AreEqual(r1.TotalSalary, emplist[0].TotalSalary);
Assert.AreEqual(r1.BasicSalary, emplist[0].BasicSalary);
I want to compare two lists emplist and result. Assert.AreEqual(r1.Name, emplist[0].Name); worked but if we have thousands of records then I need to write thousands of lines.
so please answer-- for one line code for compare two list...
thanks in advance
If I understand correctly, you simply have two instances of List<Employee> and you want to assert that they are equal. I.e. they have the same number of Employee instances, in the same order, with equal properties.
If so, this has nothing to do with FakeItEasy. You simply need a method of performing the assertion. Personally I would do this using Fluent Assertions.
result.Should().BeEquivalentTo(emplist);
You can use HashSet<> to compare the two lists.
First, define an equaliser like this
public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
//You can implement the equal method as you like. For instance, you may compare by name
if (x.Id == y.Id)
return true;
return false;
}
public int GetHashCode(Employee employee)
{
return employee.Id.GetHashCode();
}
}
Next, create 2 hash sets based on the input and the result of the method
var equaliser = new EmployeeComparer();
HashSet<Employee> inputHashset = new HashSet<Employee>(emplist ,equaliser );
HashSet<Employee> resultHashset = new HashSet<Employee>(result,equaliser);
Finally, assert the equality of the two sets. It means, instead of
Assert.AreEqual(emplist, result);
Do
Assert.IsTrue(inputHashset.SetEquals(resultHashset));
public bool compareTwolist<T>(List<T> lst1,List<T> lst2)
{
bool bresult = false;
if (lst1.GetType() != lst2.GetType())
{
return false;
}
//if any of the list is null, return false
if ((lst1 == null && lst2 != null) || (lst2 == null && lst1 != null))
return false;
//if count don't match between 2 lists, then return false
if(lst1.Count != lst2.Count)
return false;
foreach (T item in lst1)
{
T obj1 = item;
T obj2 = lst2.ElementAt(lst1.IndexOf(item));
Type type = typeof(T);
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
string obj1Value = string.Empty;
string obj2Value = string.Empty;
if (type.GetProperty(property.Name).GetValue(obj1) != null)
obj1Value = type.GetProperty(property.Name).GetValue(obj1).ToString();
if (type.GetProperty(property.Name).GetValue(obj2) != null)
obj2Value = type.GetProperty(property.Name).GetValue(obj2).ToString();
//if any of the property value inside an object in the list didnt match, return false
if (obj1Value.Trim() != obj2Value.Trim())
{
bresult = false;
break;
}
}
}
return bresult;
}
yes, I got a solution by creating user define function and pass two lists which we want to compare..
and just check is var a is true or not
var a = compareTwolist(empwithoutid, resultwithoutid);
Assert.IsTrue(a);
The comparison methods suggested in the other answers require you to implement equals on all your objects. This doesn't scale well from a maintenance perspective. Also in some tests only subset of fields are to be compared. This means multiple compare or equals methods to be implemented. An alternative is stateprinter which dumps state to a string to compare against. It can even write and rewrite your asserts for you. See https://github.com/kbilsted/StatePrinter/blob/master/doc/AutomatingUnitTesting.md for more info on automation and https://github.com/kbilsted/StatePrinter/blob/master/doc/TheProblemsWithTraditionalUnitTesting.md for an in depth discussion on problems with unit testing as you are facing now.

Wildcard search in using Lambda in EF

I have a search with optional arguments:
string id1 = HttpContext.Current.Request["id1"];
string id2 = HttpContext.Current.Request["id2"];
List<Foo> list = context.Foo.Where(l =>
(string.IsNullOrEmpty(id1) || l.Id1.Contains(id1)) &&
(string.IsNullOrEmpty(id2) || l.Id2.Contains(id2)))
.Take(10)
.ToList());
I would like to extend it so that if the string starts with a *, the EndsWith() - method should be used.
For example, if the first searchstring is *123, I want to do a l.Id1.EndsWith("123").
Is there any way to extend my current code, or should I use a different approach?
I'm quite sure if this is what you were intending, but you can break your logic up by using IQueryable(T). The query won't be executed until you try to use the collection.
public IList<Foo> Search(DbContext<Foo> context, string id1, string id2)
{
Func<Foo, bool> predicate = l =>
(string.IsNullOrEmpty(id1) || l.Id1.Contains(id1))
&& (string.IsNullOrEmpty(id2) || l.Id2.Contains(id2)));
IQueryable<Foo> list = context.Foo.Where(predicate);
if(id1.StartsWith("*") && id1.Length > 1)
{
var searchTerm = id1.Substring(1, id1.Length);
list = list.Where(l => l.EndsWith(searchTerm));
}
return list.Take(10).ToList(); // Execution occurs at this point
}
Building up the query:
public void BasicSearch(IQueryable<foo> list, string id1, string id2)
{
Func<Foo, bool> predicate = l =>
(string.IsNullOrEmpty(id1) || l.Id1.Contains(id1))
&& (string.IsNullOrEmpty(id2) || l.Id2.Contains(id2)));
list.Where(predicate);
}
public void WildcardSearch(IQueryable<Foo> list, string id1)
{
if(!id1.StartsWith("*") || id1.Length <= 1) return;
var searchTerm = id1.Substring(1, id1.Length);
list.Where(l => l.EndsWith(searchTerm));
}
IQueryable<Foo> list = context.Foo;
BasicSearch(list, id1, id2);
WildcardSearch(list, id1);
var result = list.Take(10); // Execution occurs at this point

Categories