Using custom methods in linq to entities - c#

I have a Person table in my database that has NationalId field.
Is there any way to load all Persons with even NationalId, using Ef code first and Linq to entities, without loading all Persons to memory?
Somethings like:
public bool IsEven(int number)
{
return number % 2 == 0;
}
var context = new MyContext();
var personsWithEvenNationalId = context.Persons
.Where(x=> IsEven(x.NationalId))
.ToList();

You would have to do your check inline
var personsWithEvenNationalId = context.Persons
.Where(x=> x.NationalId%2 == 0)
.ToList();
Linq to Entities doesn't know how to translate your custom method into SQL basically. If you do need to use a custom method you would have to get Persons as enumerable and then use your custom method, i.e.
var personsWithEvenNationalId = context.Persons
.AsEnumerable()
.Where(x=> IsEven(x.NationalId))
.ToList();
But that's hardly ideal as it would load all Persons and then filter on IsEven
Edit: Thinking about it you could also create an extension method for IQueryable<Person> if you don't want to have to write it inline every time. Something like this where you build an Expression
public static IQueryable<Person> WhereEven(this IQueryable<Person> source, Expression<Func<Person, int>> property)
{
var expression = Expression.Equal(
Expression.Modulo(
property.Body,
Expression.Constant(2)),
Expression.Constant(0));
var methodCallExpression = Expression.Call(typeof (Queryable),
"where",
new Type[] {source.ElementType},
source.Expression,
Expression.Lambda<Func<Person, bool>>(expression, property.Parameters));
return source.Provider.CreateQuery<Person>(methodCallExpression);
}
And to use it:
context.Persons.WhereEven(x => x.NationalId).ToList();

Rather than a function that does what you want, you'll need to have an function (or property, or field) that provides an Expression that does the projection that you want:
public static Expression<Func<int, bool>> IsEven()
{
return number => number % 2 == 0;
}
You can now write:
using(var context = new MyContext())
{
var personsWithEvenNationalId = context.Persons
.Select(x=> x.NationalId)
.Where(IsEven())
.ToList();
}

Related

C# - How to use Expression<Func> with parametrs in LINQ where

Task: I need to give some expression with parameters into LINQ's where to get some data from database, but have an error above
This example of working expression:
var shopExp = GetPersonForShop(PersonTypeIds.Director, new Guid("adda423f-8c38-40e0-9f39-6deceb787bc0")); // id
Where(shopExp)
But i need assign id dynamically, but got error above :
_repository.Persons
.Where(GetPersonForShop(PersonTypeIds.Director, person.PersonId)
And got error:
{"Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression2' to type 'System.Linq.Expressions.LambdaExpression'."}
How does function for where(linq) look:
private Expression<Func<Person, bool>> GetPersonForShop(PersonTypeIds personTypeId, Guid personId)
{
return person => person .PeronTypeId== (int) personTypeId && person .PersonId == personId;
}
This is approximate look like out production, just change names of parametrs code
How can I add expression with parameters to Where clause??
Lambda expressions use => notation. Try something like this:
var idToFind = new Guid("adda423f-8c38-40e0-9f39-6deceb787bc0");
var result = _repository.Persons
.Where(p => p.TypeId == PersonTypeIds.Director && p.PersonId == idToFind);
In this expression, p represents each Person record in the Persons table, compared one-by-one using the boolean expression that follows it.
Depending on your datasource, the comparison for each p will either be done by .NET in memory, or it will happen inside your database using a SQL WHERE clause which is constructed from the boolean expression. The last would be optimal because it would mean that not the entire Persons table has to be transferred into .NET memory before comparison can take place.
Update - To apply the same condition multiple times without repeating it in your code, while still keeping the advantages of LINQ to SQL translation intact, you can put the condition in an Expression<Func<Person, bool>> object and then use that multiple times:
Expression<Func<Person, bool>> expression =
p => p.TypeId == PersonTypeIds.Director && p.PersonId == idToFind;
var result1 = datasource1.Where(expression);
var result2 = datasource2.Where(expression);
var result3 = datasource3.Where(expression);
Or through a method that produces the Expression object:
var result1 = datasource1.Where(GetExpression(idToFind));
var result2 = datasource2.Where(GetExpression(idToFind));
var result3 = datasource3.Where(GetExpression(idToFind));
public Expression<Func<Person, bool>> GetExpression(Guid idToFind)
{
return p => p.TypeId == PersonTypeIds.Director && p.PersonId == idToFind;
}
Or alternatively you can use a helper method:
var result1 = FilterByTypeAndId(datasource1, idToFind);
var result2 = FilterByTypeAndId(datasource2, idToFind);
var result3 = FilterByTypeAndId(datasource3, idToFind);
public IQueryable<Person> FilterByTypeAndId(IQueryable<Person> datasource, Guid idToFind)
{
return datasource.Where(p => p.TypeId == PersonTypeIds.Director && p.PersonId == idToFind);
}
based on the previous response, I am going to give you a few alternatives and suggestions.
var idToFind = new Guid("adda423f-8c38-40e0-9f39-6deceb787bc0");
var result = _repository
.Persons
.Where(p => p.TypeId == PersonTypeIds.Director)
.Where(p => p.PersonId == idToFind)
.ToList();
First is doing the where clause in 2 steps and then, adding the ToList(), with the ToList(), you will deal with collections and LINQ that is pretty useful. And by doing the where clause in 2 steps, is more for readable purposes.

Linq query takes too long when using Func & OrderByDescending

Consider this code:
public List<Clients> GetFilteredClients(DateTime? FromDate = null,
DateTime? ToDate = null,
int? fromLocationType = null,
int? toLocationType = null)
{
Func<Clients, bool> fromDateFilter = f => true;
if (FromDate.HasValue)
{
fromDateFilter = z => z.Insert_Date.Value.Date >= FromDate.Value.Date;
}
Func<Clients, bool> toDateFilter = f => true;
if (ToDate.HasValue)
{
toDateFilter = z => z.Insert_Date.Value.Date <= ToDate.Value.Date;
}
Func<Clients, bool> fromLocationTypeFilter = f => true;
if (fromLocationType.HasValue)
{
fromOrgFilter = z => z.LocationTypeId >= fromLocationType.Value;
}
Func<Clients, bool> toLocationTypeFilter = f => true;
if (toLocationType.HasValue)
{
toLocationTypeFilter = z => z.LocationTypeId <= toLocationType.Value;
}
var filtered = DB_Context.Clients
.Where(fromDateFilter)
.Where(toDateFilter)
.Where(fromLocationTypeFilter)
.Where(toLocationTypeFilter)
.OrderByDescending(k => k.Id)
.Take(1000)
.ToList();
return filtered;
}
I have something like 100K records in the DB, I need only the top 1000 that answer to the requirements of:
.Where(fromDateFilter)
.Where(toDateFilter)
.Where(fromLocationTypeFilter)
.Where(toLocationTypeFilter)
However the execution time still takes something like 10 seconds.
Any idea why?
You must use Expression<Func<...>> rather than Func<...>. When you use Func, only the enumerable methods can be used on the queryable, which in this case means you first download everything to memory, and then do the filtering. If you switch over to Expression<...>, the O/RM will do the filtering on the DB server, rather than in your application.
Also, there's better ways to do what you're doing. For example, you can build the conditions like so:
var query = DB_Context.Clients.AsQueryable();
if (FromDate.HasValue) query = query.Where(...);
if (ToDate.HasValue) query = query.Where(...);
...
return query.OrderByDescending(k => k.Id).Take(1000).ToList();
Of course, this means that whatever DB provider you're using must be able to support the kind of filtering you're trying to do - you'll need to consult the documentation.
You are using delegates instead LINQ expressions. That leads to processing a data by your application and not by SQL Server.
LINQ expressions look like lambda expressions thanks for the syntax, but they are not same thing. The compiler takes a decision what to create (delegates or LINQ expressions) depending on the situation.
If an object implements the IQueriable interface, then the compiler uses the Queryable class and generates LINQ expression trees, which later can be translated into a SQL query or other form by the specific IQueryProvider.
Otherwise, the compiler uses extensions from the Enumerable class, which create iterators over source collection (all records from the table in your case).
As an example. The code bellow will be compilled into LINQ expressions.
// Source code
IQueryable<Clients> source = null;
IQueryable<Clients> result = source.Where(c => c.LocationTypeId >= 1);
// Compiller generated code
IQueryable<Clients> source = null;
Expression parameterC = Expression.Parameter(typeof(Clients), "c");
IQueryable<Clients> result = Queryable.Where<Clients>(
source,
Expression.Lambda<Func<Clients, bool>>(
Expression.LessThanOrEqual(
Expression.Property(
parameterC ,
typeof(Clients).GetProperty("LocationTypeId").GetGetMethod()
),
Expression.Constant(1, typeof(int))
),
new ParameterExpression[]
{
parameterC
}
);
And this code uses delegates:
// Source code
IQueryable<Clients> source = null;
Func<Clients, bool> filter = c => c.LocationTypeId >= 1;
IEnumerable<Clients> result = source.Where(filter );
// Compiller generated code
IQueryable<Clients> source = null;
Func<Clients, bool> filter = c => c.LocationTypeId >= 1;
IEnumerable<Clients> result = Enumerable.Where(source, filter);
So, to solve you problem use Expression<Func<Clients, bool>> instead of Func<Clients, bool>:
IQueryable<Clients> result = DB_Context.Clients;
if (someFilter.HasValue)
result = result.Where(c => c.SomeProperty == someFilter.Value);
// other filters
return query
.OrderByDescending(k => k.Id)
.Take(1000)
.ToList();

How can I rewrite this LINQ query with reflection

So I had written this LINQ query using reflection, and later found out it isn't supported. What would be the best way to get the same functionality from this code?
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>().Where(p => typeof(Profile)
.GetProperty(handler.Name + "UUID").GetValue(p) == obj.uuid).ToListAsync();
Use the reflection to create the query, not in the query. Consider:
public static IQueryable<Profile> Filter(
this IQueryable<Profile> source, string name, Guid uuid)
{
// .<name>UUID
var property = typeof(Profile).GetProperty(name + "UUID");
// p
var parExp = Expression.Parameter(typeof(Profile));
// p.<name>UUID
var methodExp = Expression.Property(parExp, property);
// uuid
var constExp = Expression.Constant(uuid, typeof(Guid));
// p.<name>UUID == uuid
var binExp = Expression.Equal(methodExp, constExp);
// p => p.<name>UUID == uuid
var lambda = Expression.Lambda<Func<Profile, bool>>(binExp, parExp);
// source.Where(p => p.<name>UUID == uuid)
return source.Where(lambda);
}
This builds up the expression first (so if name was "Test" it would create the expression corresponding with p => p.TestUUID == uuid and then uses that in the call to Where.
Because this step is done first, rather than within the expression itself, there's no need for the query engine to try to translate typeof or GetProperty() into SQL (which it of course, couldn't do).
So:
var filtered = MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid);
Returns an IQueryable<Profile> with the appropriate Where attached. And so:
var profilesFromUUID = await MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid).ToListAsync();
Will as a whole first use reflection to build the query, then apply the query, then produce a list from it asynchrously and then wait for its results.
It's worth noting that since Filter() will accept any IQueryable<Profile> they can be either chained or unioned. So:
MobileService.GetTable<Profile>().Filter("A", uuid0).Filter("B", uuid1);
Is equivalent to:
from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 && p.BUUID == uuid1
And:
MobileService.GetTable<Profile>().Filter("A", uuid0).Union(
MobileService.GetTable<Profile>().Filter("B", uuid1))
Is equivalent to:
from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 || p.BUUID == uuid1
A more generalised version would be:
public static IQueryable<TSource> FilterByNamedProperty<TSource, TValue>(this IQueryable<TSource> source, string propertyName, TValue value)
{
var property = typeof(TSource).GetProperty(propertyName);
var parExp = Expression.Parameter(typeof(TSource));
var methodExp = Expression.Property(parExp, property);
var constExp = Expression.Constant(value, typeof(TValue));
var binExp = Expression.Equal(methodExp, constExp);
var lambda = Expression.Lambda<Func<TSource, bool>>(binExp, parExp);
return source.Where(lambda);
}
Then while you have to do the + "UUID" in the calling code, you can use this to do analogous queries with any IQueryable<> of any element type.
How about just compare all property name? By definition UUID would not have collision anyway. Since Profile is just a data class, the # of the property for UUID is fixed.
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
.Where(p =>
p.A_UUID == obj.uuid ||
p.B_UUID == obj.uuid ||
p.C_UUID == obj.uuid)
.ToListAsync();
Or add a method (extension method) for Profile like:
public static Guid GetUUIDByTableName(this Profile value, string tableName)
{
switch (tableName)
{
case "A_": return value.A_UUID;
case "B_": return value.B_UUID;
default: return Guid.Empty;
}
}
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
.Where(p => p.GetUUIDByTableName(handler.Name) == obj.uuid)
.ToListAsync();

Generate LINQ or Lambda Expression for Multiple columns based on the user selection of checkboxes

I have a few checkboxes on top of my webpage which correspond to the columns of my Table. Like there is a column studentId then there will be a checkbox studentId and so on. I want to write such a linq/lamda expression for List which will filter the on the basis of the checkboxes selected. For example if selects studentId and studentType checkbox then linq/lambda expression should bring all the rows matching the selection.
example:
If studentId and studentType checked then:
foreach (Child c in SomeList)
{
if (chkStudentId.checked)
{
List.FindAll (h=> h.StudentId == c.studentId);
}
if (chkStudentType.checked)
{
List.FindAll (h => h.StudentType == c.studentType)
}
}
}
I can't figure out how am I going to write such a code that if user selects multiple checkboxes, the query should compare to all the columns and bring the values based only on the checkboxes checked. The above is only static and does not help. please help. Thanks.
Expression trees are a great help if you want your query to be totally dynamic. But if the number of checkboxes is static you can also choose for the following solution:
var students = <your list of students>.AsQueryable();
if ( chkStudentId.checked)
{
students = students.Where(s => s.StudentId == c.StudentId);
}
if (chkStudentType.checked))
{
students = students.Where(s => s.StudentType== h.StudentType);
}
In such a way you can combine the where clauses in a dynamic way.
Disclaimer: I'm fairly new to this and there is probably much better ways to do this. Any feedback is higlhy appreciated.
Note that this method has no error/null checking. Since it uses reflection, you should profile it if you plan to use it in production.
public static IEnumerable<T> Filter<T>(IEnumerable<T> collection, Dictionary<string, object> filters) {
var type = typeof (T);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var queryable = collection.AsQueryable();
var instance = Expression.Parameter(type, "instance");
var expressions = new Stack<Expression>();
foreach (var filter in filters) {
var propertyName = filter.Key;
var property = properties.FirstOrDefault(x => x.Name == propertyName);
if (property == null)
continue;
var left = Expression.Property(instance, property);
var right = Expression.Constant(filter.Value, property.PropertyType);
var expr = Expression.Equal(left, right);
expressions.Push(expr);
}
Expression call = null;
Expression previousExpression = null;
while(expressions.Count > 0) {
var expr = expressions.Pop();
if(previousExpression == null) {
previousExpression = expr;
call = expr;
} else {
var and = Expression.AndAlso(previousExpression, expr);
call = and;
previousExpression = and;
}
}
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<T, bool>>(call, new[] { instance }));
return queryable.Provider.CreateQuery<T>(whereCallExpression);
}
It overates all filters and tries to find a matching property. If it does find a property, it creates a EqualExpression which compares the actual value and the value you want to filter by. It then creates a MethodCallExpression which is passed to the query provider.
Theese expressions are then combined. I think the part with the stack is wrong, and that there is a better way to do it.
Usage:
var persons = new List<Person> {new Person {Name = "Alex", Age = 22}, new Person {Name = "Jesper", Age = 30}};
var filters = new Dictionary<string, object>();
filters.Add("Name", "Alexander Nyquist");
var results = Filter(persons, filters);
Since it's building expressions, it does works with Linq 2 Sql (tested) and probably Entity Framework. Linq 2 sql produces the following query:
SELECT [t0].[Id], [t0].[Name], [t0].[Email]
FROM [dbo].[Persons] AS [t0]
WHERE [t0].[Name] = #p0
-- #p0: Input VarChar (Size = 8000; Prec = 0; Scale = 0) [Alexander Nyquist]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
Hope this helps.
From your description of your problem it seemed fairly straight forward that you have a list of predicates, that are enabled or disable based on the check boxes, and then you want to aggregate them to create a single composite filter. Easy!
Your predicates look like this:
h => h.StudentId == c.studentId
h => h.StudentType == c.studentType
But because of the check boxes, you really want them to look like this:
h => chkStudentId.Checked ? h.StudentId == c.studentId : true
h => chkStudentId.Checked ? h.StudentType == c.studentType : true
You're effectively extending the predicates to include the check box. Here's a function that does that:
Func<CheckBox, Func<Student, bool>, Func<Student, bool>> extend =
(cb, p) =>
s => cb.Checked ? p(s) : true;
Now you can write your list of predicates like this:
var predicates = new Func<Student, bool>[]
{
extend(chkStudentId, h => h.StudentId == c.studentId),
extend(chkStudentType, h => h.StudentType == c.studentType),
// etc
};
Next LINQ has an easy way to turn a list of somethings into a single something:
Func<Student, bool>
predicate =
predicates.Aggregate((a, p) => s => a(s) && p(s));
Now you just have to execute this code to get your filtered list:
var filtered = SomeList.Where(predicate);
Now anytime any of the check boxes change you just need to enumerate the filtered collection and you'll get your filtered results.
Expression Trees can do what you want, but they are rather complicated. They allow you to dynamically build the linq query.
http://msdn.microsoft.com/en-us/library/bb882637.aspx
http://msdn.microsoft.com/en-us/library/bb397951.aspx
The Dynamic Linq Library may also be of use. I haven't personally used it though.
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The solution that worked for me was a bit modification of what Wouter de Kort
suggest and was like this:
var students = StudentList.AsQueryable();
foreach (Student sc in GroupingList)
{
students = students.Where(s => (chkStudentId.Checked? h.StudentId.Trim() == sc.StudentId.Trim() : true) && (chkStudentType.Checked? h.StudentType.Trim() == sc.StudentType.Trim() : true) );
}

How to refactor multiple similar Linq-To-Sql queries?

Suppose I have the two following Linq-To-SQL queries I want to refactor:
var someValue1 = 0;
var someValue2= 0;
var query1 = db.TableAs.Where( a => a.TableBs.Count() > someValue1 )
.Take( 10 );
var query2 = db.TableAs.Where( a => a.TableBs.First().item1 == someValue2)
.Take( 10 );
Note that only the Where parameter changes. There is any way to put the query inside a method and pass the Where parameter as an argument?
All the solutions posted in the previous question have been tried and failed in runtime when I try to enumerate the result.
The exception thrown was: "Unsupported overload used for query operator 'Where'"
Absolutely. You'd write:
public IQueryable<A> First10(Expression<Func<A,bool>> predicate)
{
return db.TableAs.Where(predicate).Take(10);
}
(That's assuming that TableA is IQueryable<A>.)
Call it with:
var someValue1 = 0;
var someValue2= 0;
var query1 = First10(a => a.TableBs.Count() > someValue1);
var query2 = First10(a => a.TableBs.First().item1 == someValue2);
I believe that will work...
The difference between this and the answers to your previous question is basically that this method takes Expression<Func<T,bool>> instead of just Func<T,bool> so it ends up using Queryable.Where instead of Enumerable.Where.
If you really want reusability you can try to write your own operators. E.g. instead of repeatedly writing:
var query =
Products
.Where(p => p.Description.Contains(description))
.Where(p => p.Discontinued == discontinued);
you can write simple methods:
public static IEnumerable<Product> ByName(this IEnumerable<Product> products, string description)
{
return products.Where(p => p.Description.Contains(description));
}
public static IEnumerable<Product> AreDiscontinued(IEnumerable<Product> products, bool isDiscontinued)
{
return products.Where(p => p.Discontinued == discontinued);
}
and then use it like this:
var query = Products.ByName("widget").AreDiscontinued(false);

Categories