I want to take some elements by checking them with my custom function.
I have Person table:
public class Person
{
public int Id { get; set; }
public DateTime BirthDay { get; set; }
public string Name { get; set; }
...
}
I should to use my GetAge() and other functions to filter Persons list.
My following code doesnt work:
public List<Person> FilterPersons(int ageFrom, int ageTo...etc..)
{
var all = Database.Persons.AsQueryable();
all = from item in all
where GetAge(item.BirthDay) > ageFrom
select item;
all = from item in all
where GetAge(item.BirthDay) < ageTo
select item;
// other operations
...
}
I think I can write so. In every step to do this:
List<Person> newList = new List<Person>();
foreach (var item in all)
{
var itemAge = Common.GetAge(item.BirthDay);
if (itemAge > AgeFrom)
{
newList.Add(item);
}
}
all = newList.List();
But this is not best way I think, because I should do filter by many criteries. It will work with low speed.
How can I use my functions in Linq query?
Edit:
I showed GetAge() function for example. I have many functions like that. I wanted to know how to use my function.
Well, you can't.
If you want to have criteria used in Where clause of your SQL query, you need to write them directly as a linq.Expression so that entity may parse it and transform it into SQL, not an external function.
Somthing like this works :
DateTime date = DateTime.Now.AddDays(ageFrom);
all = from item in all
where item.BirthDay > date
select item;
Query Expressions are built in to the C# compiler and as such, it only understands the expression that are built in to the compiler.
For example, when you use the where keyword, it converts that to a call to the Where<TSource>(this IQueryable<TSource> source, Func<TSource, bool> predicate) method.
This is true of Linq To Objects and Linq To SQL. What's more, with Linq To SQL, the compiler then has to convert the Query Expression to SQL, which has no way of knowing the definition of your GetAge method.
Or you can use this syntax:
DateTime date = DateTime.Now.AddDays(ageFrom);
all = item.Where(x => x.BirthDay > date).ToList();
Why not use a List<Person>.FindAll() method and pass in a method filter as the predicate?
You would use the method like this.
List<Person> filteredPersons = allPersons.FindAll(FilterPersons);
Below is the a sample method you would use as your filter.
bool FilterPersons(Person p)
{
if(//enter criteria here to determine if you want to select the person)
return true;
else
return false;
}
To do what you want this may be the code you need.
bool FilterPersons(Person p)
{
var itemAge = Common.GetAge(item.BirthDay);
if( itemAge > AgeFrom )
return true;
else
return false;
}
Assuming you can apply filters on the result:
You can apply normal filters ( in linq expressions ) and than apply your functions on the result. Of course, you need to refactor your methods.
Something like this :
var result= Users.Where(s=>s.Name).ToList();
result= MyFilter(result);
Related
lets say I have a list of CustomObjects such that
List<CustomObject> list = new List<CustomObject>();
public class CustomObject
{
public int page {get;set;}
public bool configured {get;set;}
public bool searchable {get;set;}
<more bool properties>
}
and I want to be able to sort or filter by these boolean values. But not all of the properties are required. so, some are nullable. I would like to build a dynamic query that simply chains together OrderBy() and ThenBy()
I thought maybe I should create a tuple to help.
List<Tuple<string, bool>> expressionTuple = new List<Tuple<string, bool>>();
// so now I have a list of tuples.
// so based on whether or not my method parameters are null or not I populate the list
if(ShouldFilter(methodParameter))
{
expressionTuple.Add(new Tuple<string, bool>("ColumnName", methodParameter)));
}
So I was thinking about some kind of chanined custom sort function like so:
private IEnumerable<T> CustomSort<T>(IEnumerable<T> data, List<Tuple<string, bool>> sortExpression)
{
for(int i = 0; i < sortExpression.Count; i++)
{
int index = i; // see if this is the first iteration
// build an expression that would be similar to:
if(index==0)
{
data = list.OrderBy(x=>x.ColumnName == booleanValue);
}else
{
data = list.ThenBy(x=>x.ColumnNam == booleanValue);
}
}
}
I'm not sure how to accomplish this. maybe there is an easier way??
Why? If you're using different orderings on different places, just put the LINQ-expressions at these various places?
Seems to me like you're trying to complicate something that's not really that complicated.
data.OrderBy(p => p.Page)
.ThenBy(p => p.Configured)
.ThenBy(...);
I've got a collection of movies which have various properties (title, release year, rating, etc) that I need to search for using a LINQ query as follows:
public BindingList<Movie> SearchByTitle(string title)
{
var matches = from movies in movieCollection
where movies.Title == title
select movies;
// do some other stuff with the matches
}
But I don't want a separate method to search for each property since the only thing that changes between searches is the where section. For example where movies.Rating == rating or where movies.ReleaseYear == releaseYear. How do I make the search method reusable for all different kinds of searches by passing in some sort of Expression or Func as the where section?
How do I make the search method reusable for all different kinds of searches by passing in some sort of Expression or Func as the where section?
Your query really isn't anything other than the where clause. But you can easily make the where part configurable... just not using query expressions.
public BindingList<Movie> SearchByTitle(Expression<Func<Movie, bool>> predicate)
{
var matches = movies.Where(predicate);
// Do common stuff with the matches.
}
EDIT: I was assuming that movies was an IQueryable<T>, given that you were talking about Expression. If it's just an IEnumerable<T>, you want:
public BindingList<Movie> SearchByTitle(Func<Movie, bool> predicate)
{
var matches = movies.Where(predicate);
// Do common stuff with the matches.
}
You can use an extension method (define this in a static class)
public static IQueryable<T> AddSearchParameter<T>(this IQueryable<T> query, bool condition, System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
if (condition)
{
query = query.Where(predicate);
}
return query;
}
So for example:
public BindingList<Movie> Search(string title, int? year, int? rating)
{
var matches = movieCollection.AddSearchParameter(!string.IsNullorEmpty(title), m=>m.Title == title);
matches = matches.AddSearchParameter(year.HasValue, m=>m.Year == year.Value);
matches = matches.AddSearchParameter(rating.HasValue, m=>m.rating >= rating.Value);
// do some other stuff with the matches
}
If you're using this against a database it will not actually execute the query until you enumerate so this will not make multiple calls to your database.
You could use a CompiledQuery.
Check this very interesting answer on SO.
Hope it helps.
I am trying to build a generic query mechanism to access my repository. I wish to use Lambda expressions to filter and sort the query. I am struggling to find a way to pass a list of generic Lambda expressions in, specifically for the order-by, and would appreciate help in doing so.
EDIT: 2 requirements I am trying to meet is, not expose IQueryable beyond the repository, but still be able to carry out some filtering and sorting at database level.
To better illustrate this let me show you the code
public class Query<T>
{
public class OrderBy<T>
{
public Expression<Func<T, **int**>> Clause { set; get; } // Order By clause
public bool Descending = true;
}
public Expression<Func<T, bool>> Where { set; get; } // Where clause
public IList<OrderBy<T>> OrderBys { set; get; } // Where clause
public Query()
{
OrderBys = new List<OrderBy<T>>();
}
}
public IEnumerable<Person> FindBy(Query<Person> query)
{
IQueryable<Person> Temp = GetObjectSet();
if (query.Where != null)
Temp = Temp.Where(query.Where);
foreach (var OrderByThis in query.OrderBys)
{
if (OrderByThis.Descending)
Temp = Temp.OrderByDescending(OrderByThis.Clause);
else
Temp = Temp.OrderBy(OrderByThis.Clause);
}
return Temp.ToList<Person>();
}
This is all very nice, BUT Expression< Func< T, int>> is not generic. I need to be able to do something like:
Query<Person> query = new Query<Person>();
Query<Person>.OrderBy<Person> clause1 = new Query<Person>.OrderBy<Person>();
clause1.Clause = m => m.Username;
Query<Person>.OrderBy<Person> clause2 = new Query<Person>.OrderBy<Person>();
clause2.Clause = m => m.DateOfBirth;
query.OrderBys.Add(clause1);
query.OrderBys.Add(clause2);
i.e. adding a number of different fields of different types.
I imagine there must be a way to store these as generic Lambda functions, and then in the repository convert then to the strongly typed Lambda function it needs.
How can I do this?
As I noted in my answer to your other question, I would discourage this approach in general. It makes more sense just to expose IQueryable<T>/IOrderedQueryable<T>.
That being said, there is a solution along the lines of your intention available in the selected answer to How to pass multiple Expressions to OrderBy for EF? .
It allows you to use a syntax like:
var query = context.Users ... ;
var queryWithOrderBy = ApplyOrderBy(query,
new OrderByExpression<User, string>(expression: u => u.UserName, descending: false), // a string, asc
new OrderByExpression<User, int>(expression: u => u.UserId, descending: true)); // an int, desc
var result = queryWithOrderBy.ToList(); // didn't throw an exception for me
Elaborating on my comment, I don't see why you need to construct your own intermediate query object out of Expressions and then reconstruct Expressions from that intermediate object, when you could just skip that translation altogether.
Given your example query:
repository.FindBy(people => people.OrderBy(p => p.Username).ThenBy(p => p.DateOfBirth));
Take note that you can still build up the queries incrementally, if it is being done based on user selections, for example. The following query is equivalent to the above:
Func<IEnumerable<Person>, IEnumerable<Person>> query = people => people.OrderBy(p => p.Username);
query = query.ThenBy(p => p.DateOfBirth);
I understand that you don't want to expose IQueryable beyond the repository, but you can still use LINQ with a signature such as:
public IEnumerable<Person> FindBy(Func<IEnumerable<Person>, IEnumerable<Person>> query)
{
return query(GetObjectSet()).ToList();
}
Speaking to your actual question, however, you can achieve your OrderBy task by using Expression<Func<T, object>> for the Clause property type, or if that unsettles you, you could constrain it a bit more by using IComparable instead of object, as it is really all you need for ordering, and strings and numeric types all implement it.
I have a list of objects and I'd like to update a particular member variable within one of the objects. I understand LINQ is designed for query and not meant to update lists of immutable data. What would be the best way to accomplish this? I do not need to use LINQ for the solution if it is not most efficient.
Would creating an Update extension method work? If so how would I go about doing that?
EXAMPLE:
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).Update(
trade => trade.Buy += optionQty);
Although linq is not meant to update lists of immutable data, it is very handy for getting the items that you want to update. I think for you this would be:
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).ToList().ForEach( trade => trade.Buy += optionQty);
I'm not sure if this is the best way, but will allow you to update an element from the list.
The test object:
public class SomeClass {
public int Value { get; set; }
public DateTime Date { get; set; }
}
The extension method:
public static class Extension {
public static void Update<T>(this T item, Action<T> updateAction) {
updateAction(item);
}
}
The test:
public void Test()
{
// test data
List<SomeClass> list = new List<SomeClass>()
{
new SomeClass {Value = 1, Date = DateTime.Now.AddDays(-1)},
new SomeClass {Value = 2, Date = DateTime.Now },
new SomeClass {Value = 3, Date = DateTime.Now.AddDays(1)}
};
// query and update
(from i in list where i.Date.Day.Equals(DateTime.Now.Day) select i).First().Update(v => v.Value += 5);
foreach (SomeClass s in list) {
Console.WriteLine(s.Value);
}
}
So you're expecting to get a single result here. In that case you might consider utilizing the SingleOrDefault method:
var record =
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).SingleOrDefault();
if (record != null)
record.Buy += optionQty;
Note that the SingleOrDefault method expects there to be exactly one or zero value returned (much like a row in a table for some unique primary key). If more than one record is returned, the method will throw an exception.
To create such a method, you would start with its prototype:
public static class UpdateEx {
public void Update(this IEnumerable<T> items,
Expression<Action> updateAction) {
}
}
That's the easy part.
The hard part will be to compile the Expression<Action> into an SQL update statement. Depending on how much syntax you want to support, such a compiler's complexity can range from trivial to impossible.
For an example of compiling Linq Expressions, see the TableQuery class of the sqlite-net project.
I have asked this question about using the a Linq method that returns one object (First, Min, Max, etc) from of a generic collection.
I now want to be able to use linq's Except() method and I am not sure how to do it. Perhaps the answer is just in front on me but think I need help.
I have a generic method that fills in missing dates for a corresponding descriptive field. This method is declared as below:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, string datePropertyName, string descriptionPropertyName)
{
Type type = typeof(T);
PropertyInfo dateProperty = type.GetProperty(datePropertyName);
PropertyInfo descriptionProperty = type.GetProperty(descriptionPropertyName);
...
}
What I want to accomplish is this. datePropertyName is the name of the date property I will use to fill in my date gaps (adding default object instances for the dates not already present in the collection). If I were dealing with a non-generic class, I would do something like this:
foreach (string description in descriptions)
{
var missingDates = allDates.Except(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList());
...
}
But how can I do the same using the generic method FillInMissingDates with the dateProperty and descriptionProperty properties resolved in runtime?
I think the best way would be to define an interface with all of the properties that you want to use in your method. Have the classes that the method may be used in implement this interface. Then, use a generic method and constrain the generic type to derive from the interface.
This example may not do exactly what you want -- it fills in missing dates for items in the list matching a description, but hopefully it will give you the basic idea.
public interface ITransactable
{
string Description { get; }
DateTime? TransactionDate { get; }
}
public class CompletedTransaction : ITransactable
{
...
}
// note conversion to extension method
public static void FillInMissingDates<T>( this IEnumerable<T> collection,
string match,
DateTime defaultDate )
where T : ITransactable
{
foreach (var trans in collection.Where( t => t.Description = match ))
{
if (!trans.TransactionDate.HasValue)
{
trans.TransactionDate = defaultDate;
}
}
}
You'll need to Cast your enumeration to ITransactable before invoking (at least until C# 4.0 comes out).
var list = new List<CompletedTransaction>();
list.Cast<ITransactable>()
.FillInMissingDates("description",DateTime.MinValue);
Alternatively, you could investigate using Dynamic LINQ from the VS2008 Samples collection. This would allow you to specify the name of a property if it's not consistent between classes. You'd probably still need to use reflection to set the property, however.
You could try this approach:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection,
Func<T, DateTime> dateProperty, Func<T, string> descriptionProperty, string desc)
{
return collection.Except(collection
.Where(d => descriptionProperty(d) == desc))
.Select(d => dateProperty(d));
}
This allows you to do things like:
someCollection.FillInMissingDates(o => o.CreatedDate, o => o.Description, "matching");
Note that you don't necessarily need the Except() call, and just have:
.. Where(d => descriptionProperty(d) != desc)
foreach (string description in descriptions)
{
var missingDates = allDates.Except<YourClass>(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList());
}
In fact, almost all LINQ extension in C# have a generic possible value. (Except and Except)
If you're going to identify the property to be accessed by a string name, then you don't need to use generics. Their only purpose is static type safety. Just use reflection to access the property, and make the method work on a non-generic IEnumerable.
Getting Except result with multiple properties working with custom data class is not allowed.
You have to use it like this: (given in msdn 101 LINQ Samples)
public void Linq53()
{
List<Product> products = GetProductList();
List<Customer> customers = GetCustomerList();
var productFirstChars =
from p in products
select p.ProductName[0];
var customerFirstChars =
from c in customers
select c.CompanyName[0];
var productOnlyFirstChars = productFirstChars.Except(customerFirstChars);
Console.WriteLine("First letters from Product names, but not from Customer names:");
foreach (var ch in productOnlyFirstChars)
{
Console.WriteLine(ch);
}
}
Having the key, you can handle your data accordingly :)