How to call Expression Func with two input parameters - linq

I have following Expression Func which is receiving two input paramters, first is Person Object, second is bool and returning another type of Object PersonProfile
private Exression<Func<Person, bool, PersonProfile>> PersonProfileProjection => (person, isValid) =>
new PersonProfile
{
FirstName = person.FirstName,
HasAddress = isValid ? person.Address1 : null
};
And I am trying to call this while fetching Person table from dbContext.
_dbContext.Persons.Select(PersonProfileProjection);
I am confused how to send boolean parameter inside PersonProfileProjection. It works when I only put one input and one output parameter like this. But I want extra boolean input as well.
Any help would be highly appreciated.

You can follow Microsoft documentation for this : Expression Class
One sample created for SQLite that show above function usage.
public void GetData()
{
var connection = new SQLiteConnection(#"Data Source=database.sqlite;Version=3;");
var context = new DataContext(connection);
connection.Open();
var createtableQuery = #"
drop table Company;
CREATE TABLE[Company]
(
[ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
[Seats] INTEGER NOT NULL
);
";
var command = new SQLiteCommand(createtableQuery, connection);
command.ExecuteNonQuery();
Company com = new Company()
{
Id = 6,
Seats = 7
};
context.GetTable<Company>().InsertOnSubmit(com);
context.SubmitChanges();
var companies = context.GetTable<Company>();
foreach (var company in companies)
{
Console.WriteLine("Company: {0} {1}",
company.Id, company.Seats);
}
//compile Expression using Compile method to invoke it as Delegate
Func<int,int, Company> PersonProfileProjectionComp = PersonProfileProjection.Compile();
var dd = companies.Select(p => PersonProfileProjectionComp(p.Id,p.Seats));
//// Below line inline use. Both works.
//var dd = companies.Select(p => PersonProfileProjection.Compile().Invoke(p.Id,p.Seats));
}
private System.Linq.Expressions.Expression<Func<int, int, Company>> PersonProfileProjection => (person, seats) =>
new Company
{
Id = person,
Seats = seats
};
or in one line use this :
PersonProfileProjection.Compile().Invoke(person, isValid)

You could declare it as a Func instead of an expression:
private Func<Person, bool, PersonProfile> PersonProfileProjection => (person, isValid) =>
new PersonProfile
{
FirstName = person.FirstName,
HasAddress = isValid // do what you meant to do
};
... and call it as:
_dbContext.Persons.Select(p => PersonProfileProjection(p, true));
You could as well write an ordinary method:
private PersonProfile PersonProfileProjection(Person person, bool isValid)
{
return new PersonProfile
{
FirstName = person.FirstName,
HasAddress = isValid // do what you want to do
};
}
...and call it the same way:
_dbContext.Persons.Select(p => PersonProfileProjection(p, true));

Related

How to return the new object created by linq in a generic func

I have a generic func that takes a string of fields and selects them from an object using linq. However the output is not type of object but a subset of. So to convert this new List<subsetOfOriginal> to a DataTable, I cannot use list.GetType() because it will get me Original.
I have converted
Func<T, T> CreateSelectStatement<T>(string fields)
to
Func<T, S> CreateSelectStatement<T, S>(string fields)
because I don't want TResult to be of type T. Not sure if this is the correct way. This is the code I have at the moment and how it is invoked.
Func<T, S> CreateSelectStatement<T, S>(string fields)
{
var xParameter = Expression.Parameter(typeof(T), "o");
var xNew = Expression.New(typeof(T));
var bindings = fields.Split(',').Select(o => o.Trim())
.Select(o => {
var mi = typeof(T).GetProperty(o);
var xOriginal = Expression.Property(xParameter, mi);
return Expression.Bind(mi, xOriginal);
}
);
var xInit = Expression.MemberInit(xNew, bindings);
var lambda = Expression.Lambda<Func<T, S>>(xInit, xParameter);
return lambda.Compile();
}
and then to invoke it I'm using
CreateSelectStatement<SP_ProjectAnalysis_NP, dynamic>(columns)
Expected outcome should be the fields that are loading correctly but the type of the object is as if I used p => new { }. Currently I am getting object as the return type and trying to perform the following gets me 0 properties:
private static DataTable ToDataTable<T>(IList<T> list, string tableName = "table")
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));

Can't get IEnumerable type using linq filtered query

i have been finding problems related to the following code..Actually i want to select filtered records but it gives me 0 records. I have tried the following. Please Help me..
public static IEnumerable<PostModel> GetPostData(string cat)
{
var data = new LinqClassDataContext(); IEnumerable<PostModel> pm;
pm=data.PostTables.Where(Post => new PostModel
{ CategoryName= Post.CategoryName}.Equals(cat)).Select
(Post => new PostModel
{ PostID = (int)Post.PostID,
PostSubject = Post.PostSubject,
PostAuthor = Post.PostAuthor,
PostDate = Post.PostDate,
PostContent = Post.PostContent,
CategoryName = Post.CategoryName
});
}
Why your PostModel class object would be Equals string cat?
Maybe you meant:
data.PostTables.Where(Post => Post.CategoryName == cat)
Even though you overrided Equals method on PostModel I think you should use line I meantioned above because it is more obvious.
Also at the end of query you should call .ToList() method for initiating it. And you should dispose context after creating it in the method.
public static IEnumerable<PostModel> GetPostData(string cat)
{
var data = new LinqClassDataContext();
var pm = data.PostTables.Where(post => post.CategoryName == cat)
.Select(Post => new PostModel
{
PostID = (int)Post.PostID,
PostSubject = Post.PostSubject,
PostAuthor = Post.PostAuthor,
PostDate = Post.PostDate,
PostContent = Post.PostContent,
CategoryName = Post.CategoryName
})
.ToList();
data.Dispose();
return pm;
}

The method 'OrderBy' must be called before the method 'Skip' Exception

I was trying to implement the jQgrid using MvcjQgrid and i got this exception.
System.NotSupportedException was unhandled by user code
Message=The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
Though OrdeyBy is used before Skip method why it is generating the exception? How can it be solved?
I encountered the exception in the controller:
public ActionResult GridDataBasic(GridSettings gridSettings)
{
var jobdescription = sm.GetJobDescription(gridSettings);
var totalJobDescription = sm.CountJobDescription(gridSettings);
var jsonData = new
{
total = totalJobDescription / gridSettings.PageSize + 1,
page = gridSettings.PageIndex,
records = totalJobDescription,
rows = (
from j in jobdescription
select new
{
id = j.JobDescriptionID,
cell = new[]
{
j.JobDescriptionID.ToString(),
j.JobTitle,
j.JobType.JobTypeName,
j.JobPriority.JobPriorityName,
j.JobType.Rate.ToString(),
j.CreationDate.ToShortDateString(),
j.JobDeadline.ToShortDateString(),
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
GetJobDescription Method and CountJobDescription Method
public int CountJobDescription(GridSettings gridSettings)
{
var jobdescription = _dataContext.JobDescriptions.AsQueryable();
if (gridSettings.IsSearch)
{
jobdescription = gridSettings.Where.rules.Aggregate(jobdescription, FilterJobDescription);
}
return jobdescription.Count();
}
public IQueryable<JobDescription> GetJobDescription(GridSettings gridSettings)
{
var jobdescription = orderJobDescription(_dataContext.JobDescriptions.AsQueryable(), gridSettings.SortColumn, gridSettings.SortOrder);
if (gridSettings.IsSearch)
{
jobdescription = gridSettings.Where.rules.Aggregate(jobdescription, FilterJobDescription);
}
return jobdescription.Skip((gridSettings.PageIndex - 1) * gridSettings.PageSize).Take(gridSettings.PageSize);
}
And Finally FilterJobDescription and OrderJobDescription
private static IQueryable<JobDescription> FilterJobDescription(IQueryable<JobDescription> jobdescriptions, Rule rule)
{
if (rule.field == "JobDescriptionID")
{
int result;
if (!int.TryParse(rule.data, out result))
return jobdescriptions;
return jobdescriptions.Where(j => j.JobDescriptionID == Convert.ToInt32(rule.data));
}
// Similar Statements
return jobdescriptions;
}
private IQueryable<JobDescription> orderJobDescription(IQueryable<JobDescription> jobdescriptions, string sortColumn, string sortOrder)
{
if (sortColumn == "JobDescriptionID")
return (sortOrder == "desc") ? jobdescriptions.OrderByDescending(j => j.JobDescriptionID) : jobdescriptions.OrderBy(j => j.JobDescriptionID);
return jobdescriptions;
}
The exception means that you always need a sorted input if you apply Skip, also in the case that the user doesn't click on a column to sort by. I could imagine that no sort column is specified when you open the grid view for the first time before the user can even click on a column header. To catch this case I would suggest to define some default sorting that you want when no other sorting criterion is given, for example:
switch (sortColumn)
{
case "JobDescriptionID":
return (sortOrder == "desc")
? jobdescriptions.OrderByDescending(j => j.JobDescriptionID)
: jobdescriptions.OrderBy(j => j.JobDescriptionID);
case "JobDescriptionTitle":
return (sortOrder == "desc")
? jobdescriptions.OrderByDescending(j => j.JobDescriptionTitle)
: jobdescriptions.OrderBy(j => j.JobDescriptionTitle);
// etc.
default:
return jobdescriptions.OrderBy(j => j.JobDescriptionID);
}
Edit
About your follow-up problems according to your comment: You cannot use ToString() in a LINQ to Entities query. And the next problem would be that you cannot create a string array in a query. I would suggest to load the data from the DB with their native types and then convert afterwards to strings (and to the string array) in memory:
rows = (from j in jobdescription
select new
{
JobDescriptionID = j.JobDescriptionID,
JobTitle = j.JobTitle,
JobTypeName = j.JobType.JobTypeName,
JobPriorityName = j.JobPriority.JobPriorityName,
Rate = j.JobType.Rate,
CreationDate = j.CreationDate,
JobDeadline = j.JobDeadline
})
.AsEnumerable() // DB query runs here, the rest is in memory
.Select(a => new
{
id = a.JobDescriptionID,
cell = new[]
{
a.JobDescriptionID.ToString(),
a.JobTitle,
a.JobTypeName,
a.JobPriorityName,
a.Rate.ToString(),
a.CreationDate.ToShortDateString(),
a.JobDeadline.ToShortDateString()
}
})
.ToArray()
I had the same type of problem after sorting using some code from Adam Anderson that accepted a generic sort string in OrderBy.
After getting this excpetion, i did lots of research and found that very clever fix:
var query = SelectOrders(companyNo, sortExpression);
return Queryable.Skip(query, iStartRow).Take(iPageSize).ToList();
Hope that helps !
SP

Linq - 'Saving' OrderBy operation (c#)

Assume I have generic list L of some type in c#. Then, using linq, call OrderBy() on it, passing in a lambda expression.
If I then re-assign the L, the previous order operation will obviously be lost.
Is there any way I can 'save' the lambda expression I used on the list before i reassigned it, and re-apply it?
Use a Func delegate to store your ordering then pass that to the OrderBy method:
Func<int, int> orderFunc = i => i; // func for ordering
var list = Enumerable.Range(1,10).OrderByDescending(i => i); // 10, 9 ... 1
var newList = list.OrderBy(orderFunc); // 1, 2 ... 10
As another example consider a Person class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
Now you want to preserve a sort order that sorts by the Name property. In this case the Func operates on a Person type (T) and the TResult will be a string since Name is a string and is what you are sorting by.
Func<Person, string> nameOrder = p => p.Name;
var list = new List<Person>
{
new Person { Id = 1, Name = "ABC" },
new Person { Id = 2, Name = "DEF" },
new Person { Id = 3, Name = "GHI" },
};
// descending order by name
foreach (var p in list.OrderByDescending(nameOrder))
Console.WriteLine(p.Id + ":" + p.Name);
// 3:GHI
// 2:DEF
// 1:ABC
// re-assinging the list
list = new List<Person>
{
new Person { Id = 23, Name = "Foo" },
new Person { Id = 14, Name = "Buzz" },
new Person { Id = 50, Name = "Bar" },
};
// reusing the order function (ascending by name in this case)
foreach (var p in list.OrderBy(nameOrder))
Console.WriteLine(p.Id + ":" + p.Name);
// 50:Bar
// 14:Buzz
// 23:Foo
EDIT: be sure to add ToList() after the OrderBy calls if you need a List<T> since the LINQ methods will return an IEnumerable<T>.
Calling ToList() or ToArray() on your IEnumerable<T> will cause it to be immediately evaluated. You can then assign the resulting list or array to "save" your ordered list.

How can I do an OrderBy with a dynamic string parameter?

I want to do this:
var orderBy = "Nome, Cognome desc";
var timb = time.Timbratures.Include("Anagrafica_Dipendente")
.Where(p => p.CodDipendente == 1);
if(orderBy != "")
timb = timb.OrderBy(orderBy);
Is there an OrderBy overload available that accepts a string parameter?
If you are using plain LINQ-to-objects and don't want to take a dependency on an external library it is not hard to achieve what you want.
The OrderBy() clause accepts a Func<TSource, TKey> that gets a sort key from a source element. You can define the function outside the OrderBy() clause:
Func<Item, Object> orderByFunc = null;
You can then assign it to different values depending on the sort criteria:
if (sortOrder == SortOrder.SortByName)
orderByFunc = item => item.Name;
else if (sortOrder == SortOrder.SortByRank)
orderByFunc = item => item.Rank;
Then you can sort:
var sortedItems = items.OrderBy(orderByFunc);
This example assumes that the source type is Item that have properties Name and Rank.
Note that in this example TKey is Object to not constrain the property types that can be sorted on. If the func returns a value type (like Int32) it will get boxed when sorting and that is somewhat inefficient. If you can constrain TKey to a specific value type you can work around this problem.
Absolutely. You can use the LINQ Dynamic Query Library, found on Scott Guthrie's blog. There's also an updated version available on CodePlex.
It lets you create OrderBy clauses, Where clauses, and just about everything else by passing in string parameters. It works great for creating generic code for sorting/filtering grids, etc.
var result = data
.Where(/* ... */)
.Select(/* ... */)
.OrderBy("Foo asc");
var query = DbContext.Data
.Where(/* ... */)
.Select(/* ... */)
.OrderBy("Foo ascending");
Another solution from codeConcussion (https://stackoverflow.com/a/7265394/2793768)
var param = "Address";
var pi = typeof(Student).GetProperty(param);
var orderByAddress = items.OrderBy(x => pi.GetValue(x, null));
The simplest & the best solution:
mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s));
You don't need an external library for this. The below code works for LINQ to SQL/entities.
/// <summary>
/// Sorts the elements of a sequence according to a key and the sort order.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="query" />.</typeparam>
/// <param name="query">A sequence of values to order.</param>
/// <param name="key">Name of the property of <see cref="TSource"/> by which to sort the elements.</param>
/// <param name="ascending">True for ascending order, false for descending order.</param>
/// <returns>An <see cref="T:System.Linq.IOrderedQueryable`1" /> whose elements are sorted according to a key and sort order.</returns>
public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> query, string key, bool ascending = true)
{
if (string.IsNullOrWhiteSpace(key))
{
return query;
}
var lambda = (dynamic)CreateExpression(typeof(TSource), key);
return ascending
? Queryable.OrderBy(query, lambda)
: Queryable.OrderByDescending(query, lambda);
}
private static LambdaExpression CreateExpression(Type type, string propertyName)
{
var param = Expression.Parameter(type, "x");
Expression body = param;
foreach (var member in propertyName.Split('.'))
{
body = Expression.PropertyOrField(body, member);
}
return Expression.Lambda(body, param);
}
(CreateExpression copied from https://stackoverflow.com/a/16208620/111438)
I did so:
using System.Linq.Expressions;
namespace System.Linq
{
public static class LinqExtensions
{
public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string dir = "asc")
{
// parametro => expressão
var parametro = Expression.Parameter(typeof(TSource), "r");
var expressao = Expression.Property(parametro, field);
var lambda = Expression.Lambda(expressao, parametro); // r => r.AlgumaCoisa
var tipo = typeof(TSource).GetProperty(field).PropertyType;
var nome = "OrderBy";
if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
{
nome = "OrderByDescending";
}
var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;
}
public static IOrderedQueryable<TSource> ThenBy<TSource>(this IOrderedQueryable<TSource> source, string field, string dir = "asc")
{
var parametro = Expression.Parameter(typeof(TSource), "r");
var expressao = Expression.Property(parametro, field);
var lambda = Expression.Lambda<Func<TSource, string>>(expressao, parametro); // r => r.AlgumaCoisa
var tipo = typeof(TSource).GetProperty(field).PropertyType;
var nome = "ThenBy";
if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
{
nome = "ThenByDescending";
}
var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;
}
}
}
Use :
example.OrderBy("Nome", "desc").ThenBy("other")
Work like:
example.OrderByDescending(r => r.Nome).ThenBy(r => r.other)
Look at this blog here. It describes a way to do this, by defining an EntitySorter<T>.
It allows you to pass in an IEntitySorter<T> into your service methods and use it like this:
public static Person[] GetAllPersons(IEntitySorter<Person> sorter)
{
using (var db = ContextFactory.CreateContext())
{
IOrderedQueryable<Person> sortedList = sorter.Sort(db.Persons);
return sortedList.ToArray();
}
}
And you can create an EntitiySorter like this:
IEntitySorter<Person> sorter = EntitySorter<Person>
.OrderBy(p => p.Name)
.ThenByDescending(p => p.Id);
Or like this:
var sorter = EntitySorter<Person>
.OrderByDescending("Address.City")
.ThenBy("Id");
You need to use the LINQ Dynamic Query Library in order to pass parameters at runtime,
This will allow linq statements like
string orderedBy = "Description";
var query = (from p in products
orderby(orderedBy)
select p);
If your columnName is in a variable col, then
string col="name";
list.OrderBy(x=>x[col])
As what Martin Liversage said, you can define a Func<>before you pass it to OrderBy method, but I found an interesting way to do that.
You can define a dictionary from string to Func<> like this :
Dictionary<string, Func<Item, object>> SortParameters = new Dictionary<string, Func<Item, object>>()
{
{"Rank", x => x.Rank}
};
And use it like this :
yourList.OrderBy(SortParameters["Rank"]);
In this case you can dynamically sort by string.
In one answer above:
The simplest & the best solution:
mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s));
There is an syntax error, ,null must be added:
mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s,null));

Resources