Linq query to search - c#

I have a collection of Employee class and employee class has few properties like departement,manager name,payscale,designation etc.Now in my webapi, i have a search method in which i am searching a string across all fields of webapi
like if I search Peter it'll search in all fields(departement,manager_name,payscale,designation) of all employees.For this i am using below:-
public IEnumerable<Employee> Search(string searchstr)
{
if (repository != null)
{
var query =
from employees in repository.GetEmployees()
where
(employees.departement != null && employees.departement.Contains(searchstr)) ||
(employees.payscale != null && employees.payscale.Contains(searchstr))
(movie.designation != null && movie.designation.Contains(searchstr)) )
select employees;
return query.AsEnumerable().OrderBy(c => c.employeeid);
}
else
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
Though i am getting desired result,i have not to use that query.Is there any other way to rewrite same query?

As stated by Noctis, using reflection results in a heavy task for the .NET runtime.
Here is some example code that loops trough all properties of a class and searchs a string concidence. It uses reflection ;)
Any questions, leave a comment asking!
The code in the entry point of the APP:
[STAThread]
private static void Main(string[] args)
{
var person1 = new Person {Name = "The first name", Address = "The first address"};
var person2 = new Person {Name = "The second name", Address = "The second address"};
var results = SearchStringTroughAllProperties("second", new List<Person> {person1, person2});
}
The Person class:
class Person
{
public string Name { get; set; }
public string Address { get; set; }
}
And the SearchStringTroughAllProperties method:
private static IEnumerable<Person> SearchStringTroughAllProperties(string stringToSearch,
IEnumerable<Person> persons)
{
var properties =
typeof (Person).GetProperties()
.Where(x => x.CanRead && x.PropertyType == typeof (string))
.Select(x => x.GetMethod)
.Where(x => !x.IsStatic)
.ToList();
return persons.Where(person =>
properties.Select(property => (string) property.Invoke(person, null) ?? string.Empty)
.Any(propertyValueInInstance => propertyValueInInstance.Contains(stringToSearch)));
}
Notice that:
It searchs in properties, not in fields
It only searchs on properties that can be read (have a get defined)
It only searchs on properties of type string
It only searchs on properties that aren't static members of the class
EDIT:
For searching the string coincidence in a string or string[] property, change SearchStringTroughAllProperties method to this (it gets longer!):
static IEnumerable<Person> SearchStringTroughAllProperties(string stringToSearch, IEnumerable<Person> persons)
{
var properties =
typeof (Person).GetProperties()
.Where(x => x.CanRead && (x.PropertyType == typeof (string) || x.PropertyType == typeof(string[])))
.Select(x => x.GetMethod)
.Where(x => !x.IsStatic)
.ToList();
foreach (var person in persons)
{
foreach (var property in properties)
{
bool yieldReturned = false;
switch (property.ReturnType.ToString())
{
case "System.String":
var propertyValueStr = (string) property.Invoke(person, null) ?? string.Empty;
if (propertyValueStr.Contains(stringToSearch))
{
yield return person;
yieldReturned = true;
}
break;
case "System.String[]":
var propertyValueStrArr = (string[]) property.Invoke(person, null);
if (propertyValueStrArr != null && propertyValueStrArr.Any(x => x.Contains(stringToSearch)))
{
yield return person;
yieldReturned = true;
}
break;
}
if (yieldReturned)
{
break;
}
}
}
}

Even though work, it feels a bit dirty. I would consider maybe using reflection to get the properties of the class, and then dynamically search them.
The benefit would be: if you add a new property tomorrow, there's nothing else you need to change.
The disadvantage would be: probably not as performant since reflection is much slower than simply searching for things you know exist.
Having said that, i'm sure there are some other nifty advanced linq tricks that maybe others can point out.
Accessing Attributes by Using Reflection (C# and Visual Basic)
I thought I have some handy code but I don't. I wouldn't like to write it of the top of my head, because it probably won't compile (you need to get the syntax right).
Have a look at the above link :)

Related

How to filter list of classes by property name?

I want to filter a collection of classes by the property name as a string. Let's say I have a class named Person, and I have a collection of it, either IEnumerable, or List, and I want to filter this collection, but I don't know the exact filter, I mean I can't use:
person.Where(x => x.Id == 1);
Let me give an example.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int YearOfBorn {get; set;}
}
And now I create a collection like:
List<Person> p = new List<Person>();
Now I want to filter everyone whose name is Alex, but I want to filter it using this function:
public List<Person> Filter(string propertyName, string filterValue, List<Person> persons)
So how I can filter it if I want to use Linq, or Lambda?
Thanks
Technically, you can try using Reflection:
using System.Reflection;
...
// T, IEnumerable<T> - let's generalize it a bit
public List<T> Filter<T>(string propertyName, string filterValue, IEnumerable<T> persons) {
if (null == persons)
throw new ArgumentNullException("persons");
else if (null == propertyName)
throw new ArgumentNullException("propertyName");
PropertyInfo info = typeof(T).GetProperty(propertyName);
if (null == info)
throw new ArgumentException($"Property {propertyName} hasn't been found.",
"propertyName");
// A bit complex, but in general case we have to think of
// 1. GetValue can be expensive, that's why we ensure it calls just once
// 2. GetValue as well as filterValue can be null
return persons
.Select(item => new {
value = item,
prop = info.GetValue(item),
})
.Where(item => null == filterValue
? item.prop == null
: item.prop != null && string.Equals(filterValue, item.prop.ToString()))
.Select(item => item.value)
.ToList();
}

The type appears in two structurally incompatible initializations within a single LINQ to Entities query

I'm trying to build something like conditional queries to get only needed data from the underlying database.
Currently I have the following query (which works fine)
var eventData = dbContext.Event.Select(t => new
{
Address = true ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
});
If I change it to
var includeAddress = true; // this will normally be passed as param
var eventData = dbContext.Event.Select(t => new
{
Address = includeAddress ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
});
I get the following error:
The type 'AnonymousEventGetAddress' appears in two structurally incompatible initializations within a single LINQ to Entities query.
A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
What am I doing wrong here (as of with the true it's working) and how can this be fixed?
I know that changing the else-part to
new AnonymousEventGetAddress
{
AddressLine1 = null,
CityName = null
}
will work. But if I change the order of the properties then, this will also fail.
The class used is defined the following:
public class AnonymousEventGetAddress : BaseAnonymousObject<AnonymousEventGetAddress>
{
public string AddressLine1 { get; set; }
public string CityName { get; set; }
}
whereas BaseAnonymousObject<AnonymousEventGetAddress> is defined:
public abstract class BaseAnonymousObject<TAnonymous>
where TAnonymous : BaseAnonymousObject<TAnonymous>
{
// this is used in case I have to return a list instead of a single anonymous object
public static Expression<Func<IEnumerable<TAnonymous>>> Empty => () => new TAnonymous[]{}.AsEnumerable();
}
I don't know why EF has such requirement, but the important thing is that the requirement exists and we need to take it into account.
The first code works because true is a compile time constant, so the compiler is resolving it at compile time, ending up with one of the two expressions (basically removing the ternary operator). While in the second case it's a variable, thus the expression tree contains the original expression and fails at runtime due to aforementioned EF requirement.
A while ago I was trying to solve this and similar problems (to be honest, mainly for dynamic where filters) by implementing a custom method which is trying to resolve the bool variables, thus doing at runtime something similar to what does the compiler in the first case. Of course the code is experimental and not tested, but seem to handle properly such scenarios, so you can give it a try. The usage is quite simple:
var eventData = dbContext.Event.Select(t => new
{
Address = includeAddress ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
}).ReduceConstPredicates();
And here is the helper method used:
public static partial class QueryableExtensions
{
public static IQueryable<T> ReduceConstPredicates<T>(this IQueryable<T> source)
{
var visitor = new ConstPredicateReducer();
var expression = visitor.Visit(source.Expression);
if (expression != source.Expression)
return source.Provider.CreateQuery<T>(expression);
return source;
}
class ConstPredicateReducer : ExpressionVisitor
{
int evaluateConst;
private ConstantExpression TryEvaluateConst(Expression node)
{
evaluateConst++;
try { return Visit(node) as ConstantExpression; }
finally { evaluateConst--; }
}
protected override Expression VisitConditional(ConditionalExpression node)
{
var testConst = TryEvaluateConst(node.Test);
if (testConst != null)
return Visit((bool)testConst.Value ? node.IfTrue : node.IfFalse);
return base.VisitConditional(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.Type == typeof(bool))
{
var leftConst = TryEvaluateConst(node.Left);
var rightConst = TryEvaluateConst(node.Right);
if (leftConst != null || rightConst != null)
{
if (node.NodeType == ExpressionType.AndAlso)
{
if (leftConst != null) return (bool)leftConst.Value ? (rightConst ?? Visit(node.Right)) : Expression.Constant(false);
return (bool)rightConst.Value ? Visit(node.Left) : Expression.Constant(false);
}
else if (node.NodeType == ExpressionType.OrElse)
{
if (leftConst != null) return !(bool)leftConst.Value ? (rightConst ?? Visit(node.Right)) : Expression.Constant(true);
return !(bool)rightConst.Value ? Visit(node.Left) : Expression.Constant(true);
}
else if (leftConst != null && rightConst != null)
{
var result = Expression.Lambda<Func<bool>>(Expression.MakeBinary(node.NodeType, leftConst, rightConst)).Compile().Invoke();
return Expression.Constant(result);
}
}
}
return base.VisitBinary(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (evaluateConst > 0)
{
var objectConst = node.Object != null ? TryEvaluateConst(node.Object) : null;
if (node.Object == null || objectConst != null)
{
var arguments = new object[node.Arguments.Count];
bool canEvaluate = true;
for (int i = 0; i < arguments.Length; i++)
{
var argumentConst = TryEvaluateConst(node.Arguments[i]);
if (canEvaluate = (argumentConst != null))
arguments[i] = argumentConst.Value;
else
break;
}
if (canEvaluate)
{
var result = node.Method.Invoke(objectConst != null ? objectConst.Value : null, arguments);
return Expression.Constant(result, node.Type);
}
}
}
return base.VisitMethodCall(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (evaluateConst > 0 && (node.NodeType == ExpressionType.Convert || node.NodeType == ExpressionType.ConvertChecked))
{
var operandConst = TryEvaluateConst(node.Operand);
if (operandConst != null)
{
var result = Expression.Lambda(node.Update(operandConst)).Compile().DynamicInvoke();
return Expression.Constant(result, node.Type);
}
}
return base.VisitUnary(node);
}
protected override Expression VisitMember(MemberExpression node)
{
object value;
if (evaluateConst > 0 && TryGetValue(node, out value))
return Expression.Constant(value, node.Type);
return base.VisitMember(node);
}
static bool TryGetValue(MemberExpression me, out object value)
{
object source = null;
if (me.Expression != null)
{
if (me.Expression.NodeType == ExpressionType.Constant)
source = ((ConstantExpression)me.Expression).Value;
else if (me.Expression.NodeType != ExpressionType.MemberAccess
|| !TryGetValue((MemberExpression)me.Expression, out source))
{
value = null;
return false;
}
}
if (me.Member is PropertyInfo)
value = ((PropertyInfo)me.Member).GetValue(source);
else
value = ((FieldInfo)me.Member).GetValue(source);
return true;
}
}
}
For future readers, this SO duplicate (added one year later) was key to solving my problems:
The type appear in two structurally incompatible initializations within a single LINQ to Entities query
When you look at it, the error message is abundantly clear. Don't mess up the initialization order if you are instantiating an object more than once in the same Linq expression. For me, that was exactly what I was doing. Synchronizing the property initializations between the two instantiation calls had the compiler blowing sunshine again.
In this case:
new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
}
is different from
new AnonymousEventGetAddress()
In OP query version 1 it is safe to say that the aberrant initialization in the else branch could never happen due to the the true conditional, why it was probably discarded, for version two that must not have happened, and we're left with two initialization orders, properties 1 and 2 versus no properties at all.
This should do it:
includeAddress
? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
}
: new AnonymousEventGetAddress
{
AddressLine1 = null,
CityName = null
}
In some circumstances there simple workaround might be possible: make the type appear as different types. E.g. make 2 subclasses from the original class. This workaround is of course quite dirty but the Linq requirement is artificial by itself. In my case this helped.
You can put the conditional statement in each property initializer.
var eventData = dbContext.Event.Select(t => new
{
Address = new AnonymousEventGetAddress
{
AddressLine1 = includeAddress ? t.Address.AddressLine1 : null,
CityName = includeAddress ? t.Address.AddressCityName : null
}
});
In my opinion, I always try to steer away from putting anything more complicated then selecting data into IQueryable because they're Expression, which means they're never executed - they're compiled.
So, I would tackle this problem like so (this has a nice air of simplicity around it):
Create a DTO for the return data:
public class EventDto
{
// some properties here that you need
public Address Address {get;set;}
}
Then I would split your logic around the includeAddress
public IEnumerable<EventDto> IncludeAddress(DbContext dbContext)
{
return dbContext.Event.Select(t => new
{
// select out your other properties here
Address = new
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
},
}).ToList().Select(x => new EventDto { Address = Address });
// put what ever mapping logic you have up there, whether you use AutoMapper or hand map it doesn't matter.
}
The method NoAddress or whatever you want to call it will look similar but with no Address, and you map it back.
You can then choose which one simply:
var eventDtos = new List<EventDto>();
if (includeAddress)
eventDtos.AddRange(this.IncludeAddress(dbContext));
else
eventDtos.AddRange(this.NoAddress(dbContext));
eventDtos.ForEach(e => { if (e.Address == null) e.Address = new Address(); });
If you Select does have a lot of logic in it, then I would consider moving it out into a sproc where it will be easier to read the SQL.
Obviously this is just a guide, gives you an idea on how to tackle the problem.
I had the same problem, and found the solution to be, to add .ToList() , before the select-function :
var eventData = dbContext.Event.ToList().Select(t => new
{
Address = includeAddress ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
});

How to add if statement to a LINQ query in C#?

Consider the the following query method:
internal List<Product> productInStockQuery(InStock inStock1)
{
var inStock =
from p in products
where p.INStock == inStock1
select p;
return inStock.ToList<Product>();
}
I would like the method to do the following:
If inStock1 == InStock.needToOrder I would like it to search for both (something like):
(instock1==InStock.needToOrder && instock1==InStock.False)
How can I do that?
I would also like to know if it is possible to create a method that allows me to search all of my Product fields in one Method.
EDITED SECTION: (second question i had)
trying to explain myself better: my class has several fields and right now for each field i have a method like shown above i wanted to know if it is possible to create a special variable that will allow me to acces each field in Product without actually typing the fields name or getters like instead of p.price / p.productName i would just type p.VARIABLE and depending on this variable i will acces the wanted field / getter
if such option exists i would appreciate it if you could tell me what to search the web for.
p.s
thanks alot for all the quick responses i am checking them all out.
Do you mean using something like an Enum?
internal enum InStock
{
NeedToOrder,
NotInStock,
InStock
}
internal class Product
{
public string Name { get; set; }
public InStock Stock { get; set; }
}
Then pass a collection to the method...
internal static List<Product> ProductInStockQuery(List<Product> products)
{
var inStock =
from p in products
where p.Stock != InStock.NeedToOrder && p.Stock != InStock.NotInStock
select p;
return inStock.ToList();
}
Create a list and pass it in...
var prods = new List<Product>
{
new Product
{
Name = "Im in stock",
Stock = InStock.InStock
},
new Product
{
Name = "Im in stock too",
Stock = InStock.InStock
},
new Product
{
Name = "Im not in stock",
Stock = InStock.NotInStock
},
new Product
{
Name = "need to order me",
Stock = InStock.NotInStock
},
};
var products = ProductInStockQuery(prods);
You can compose queries based upon an input variable like so:
var inStock =
from p in products
select p;
if(inStock1 == InStock.needToOrder)
{
inStock = (from p in inStock where p.INStock == InStock.needToOrder || instock1 == InStock.False select p);
}
else
{
inStock = (from p in inStock where p.INStock == inStock1 select p);
}
return inStock.ToList<Product>();
I believe you can use WhereIf LINQ extension method to make it more clear and reusable. Here is an example of extension
public static class MyLinqExtensions
{
public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Boolean condition, System.Linq.Expressions.Expression<Func<T, Boolean>> predicate)
{
return condition ? source.Where(predicate) : source;
}
public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Boolean condition, Func<T, Boolean> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
Below you can see an example how to use this approach
class Program
{
static void Main(string[] args)
{
var array = new List<Int32>() { 1, 2, 3, 4 };
var condition = false; // change it to see different outputs
array
.WhereIf<Int32>(condition, x => x % 2 == 0) // predicate will be applied only if condition TRUE
.WhereIf<Int32>(!condition, x => x % 3 == 0) // predicate will be applied only if condition FALSE
.ToList() // don't call ToList() multiple times in the real world scenario
.ForEach(x => Console.WriteLine(x));
}
}
q = (from p in products);
if (inStock1 == InStock.NeedToOrder)
q = q.Where(p => p.INStock == InStock.False || p.InStock == InStock.needToOrder);
else
q = q.Where(p => p.INStock == inStock1);
return q.ToList();

How to validate values in a list of object?

I have a List<ObjectA> populated.
Class ObjectA has two properties -string property1 bool? property2
[1]I need to loop through the entire list to check if property1 contains a particular string say 'xyz'.
[2] I need to check if value of property2 is true(which i believe i can figure out after getting to know how to handle [1])
thanks
Adarsh
With List<T> you can also use its method TrueForAll:
bool valid = list.TrueForAll(a => a.property1.Contains("xyz") && a.property2);
This will work on any version of .NET >= 2.0. You also can use LINQ as #Hassan suggested:
bool valid = list.All(a => a.property1.Contains("xyz") && a.property2);
That will work on .NET >= 3.5. Benefit of this option is ability to work with any enumerable source (i.e. if you'll change list to ICollection, or some other enumerable type).
if you want to make sure if all elements of a particular collection satisfy a condition you can use All method in Linq, like this:
new List<MyClass>().All(m => m.StringProp.Contains("ss") && m.IsValid == true)
Use LINQ:
List<ObjectA> list;
//Check for string
if (list.Any(a => a.property1.Contains("xyz")))
{
}
// Check for true
if (list.Any(a => a.property2 ?? false))
{
}
If you need to get the objects that satisfies your conditions, use list.Where(...)
There are several possible checks and results you can easily get.
void Main()
{ //ignore Dump() method
var list = new List<ObjectA>()
{
new ObjectA("name1", true),
new ObjectA("name2", true),
new ObjectA("name3", null),
new ObjectA("name4", false),
new ObjectA("name5", null),
new ObjectA("name6", false),
new ObjectA("name7", true)
};
//check if all Items are valid
var allItems = list.All(m => m.Name.Contains("ss") && m.valid == true); // false
//only get valid items (name contains name AND true)
var validItems = list.Where (l => l.Name.Contains("name") && l.valid == true).Dump();
//get all items which are invalid
var nonvalidItems = list.Where(l =>!(l.Name.Contains("name")) || l.valid != true).Dump();
}
public class ObjectA
{
public ObjectA(string name, bool? _val)
{
this.Name = name;
if(_val.HasValue)
this.valid = _val;
}
public string Name { get; set; }
public bool? valid { get; set; }
}

how to pass object's property (instead of object) into expression when building Linq2Sql query?

tl;dr: I'm looking for a way to pass object's property image.Member (instead of object image) into expression when building Linq2Sql query. This way I will have Expression<Func<Member, bool>> instead of Expression<Func<Image, bool>>.
Here is my domain model (EF 5 if that matters):
class Image
{
public string Url { get; set;}
public Member Member { get; set;} // user who uploaded the image
}
class Member
{
public int Id { get; set;}
public bool IsPrivate { get; set;}
public string AboutMe { get; set;}
public string ScreenName { get; set;}
[NotMapped]
public bool IsSearchable
{
get
{
return IsPrivate == false &&
string.IsNullOrEmpty(AboutMe) == false &&
string.IsNullOrEmpty(ScreenName) == false;
}
}
}
next, I want to get the all images from the database with the following logic:
if user is logged in, take all his images, and images of all other
members who are IsSearchable. If user is not logged in, just take
all images of all other members who are IsSearchable.
In Linq2Objects this would look like this:
var images = imagesRepository.All().ToList().AsIEnumerable();
if (this.User == null)
{
images = images.Where(x => x.Member.IsSearchable == true);
}
else
{
var currentMemberId = this.User.Id;
images = images.Where(x => x.Member.IsSearchable == true || x.Member.Id == currentMemberId);
}
and of course this is inefficient - i don't want to load 10000 images from DB just to get 10 of them. That leads me to Linq2Sql. NOTE: I understand that I can copy logic from 'IsSearchable' into 'Where' clause and I will get proper 'IQueryable' but I have 2 issues with it:
IsSearchable logic is easy to change later on - and I would never remember about this copy/paste
I will have to copy it TWICE (because currentMemberId is in OR condition) - which will make code hard to read.
That leads me to believe that the only solution I have is to use Expression. Here we go (thanks to this Expression.Or question):
var parameterExpression = Expression.Parameter(typeof(Image));
Expression<Func<Image, bool>> memberIsSearchable =
i => i.Member.IsPrivate == false &&
string.IsNullOrEmpty(i.Member.AboutMe) == false &&
string.IsNullOrEmpty(i.Member.ScreenName) == false;
Expression<Func<Image, bool>> memberById =
i => i.Member.MemberId == currentMemberId;
var combined = Expression.Lambda<Func<Image, bool>>(
Expression.Or(
Expression.Invoke(memberIsSearchable, parameterExpression),
Expression.Invoke(memberById, parameterExpression)),
parameterExpression);
var images = imagesRepository.All();
if (this.User == null)
{
images = images.Where(memberIsSearchable);
}
else
{
images = images.Where(combined);
}
this translates nicely into SQL.
And here comes the question:
is it possible to get rid of Image type in expression definition
Expression<Func<Image, bool>> memberIsSearchable = ...
and replace it with Member and somehow tell expression to use 'Member' from the 'Image' object when query is created:
Expression<Func<Member, bool>> memberIsSearchable =
m => m.IsPrivate == false &&
string.IsNullOrEmpty(m.AboutMe) == false &&
string.IsNullOrEmpty(m.ScreenName) == false;
images = images.Where(x => memberIsSearchable(x.Member)); // pseudocode
The reason for that is to make memberIsSearchable expression generic and use it in other parts of the project.
Took me a while to figure out what exactly your problem is - I think, I got it now.
Try starting at the members (instead of at the images). Maybe you need to introduce additional navgation properties:
Expression<Func<Member, bool>> IsSearchable = member => !member.IsPrivate
&& !String.IsNullOrEmpty(member.AboutMe)
&& !String.IsNullOrEmpty(member.ScreenName);
Member currentUser = null;
using (var container = new /**/())
{
var images = container.Members.Where(IsSearchable).SelectMany(member => member.Images);
if (currentUser != null)
images = images.Concat(currentUser.Images);
var result = images.ToArray();
}

Categories