how to use a dynamic variable in orderby clause - asp.net-mvc-3

am a newbie in linq.. am stuck with one scenario. ie,
i have to sort the search results based on user input.
user inputs are Last Name, First Name and Title. for input 3 drop downs are there and i have to sort result based on the values selected.
i tried
order = Request["orders"].Split(',');
var param = order[0];
var p1 = typeof(Test).GetProperty(param);
param = order[1];
var p2 = typeof(Test).GetProperty(param);
param = order[2];
var p3 = typeof(Test).GetProperty(param);
model.Test = (from tests in model.Test
select tests).
OrderBy(x => p1.GetValue(x, null)).
ThenBy(x => p2.GetValue(x, null)).
ThenBy(x => p3.GetValue(x, null));
but it doesn't works.
i want qry like this
from tests in model.Test
select tests).OrderBy(x => x.lastname).
ThenBy(x => x.firstname).ThenBy(x => x.Title);
order[0]== lastname but how can i use it in the place of OrderBy(x => x.order[0])..?
Thanks in advance.

i solved my case as follows
// list of columns to be used for sorting
List<string>order = Request["orders"].Split(',').ToList();
//map the column string to property
var mapp = new Dictionary<string, Func<Test, string>>
{
{"FirstName", x => x.FirstName},
{"LastName", x => x.LastName},
{"SimpleTitle", x => x.SimpleTitle}
};
//user inputted order
var paras = new List<Func<Test, string>>();
foreach (var para in order)
{
if(!string.IsNullOrEmpty(para))
paras.Add(mapp[para]);
}
//sorting
model.Test= model.Test.OrderBy(paras[0]).ThenBy(paras[1]).ThenBy(paras[2]);
Thanks all,

Actually you are looking for dynamic linq query than you can try out Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)
which allow to do like this
it means you can dynamically pass string propertyname to short you collection in orderby function
You can also read about : Dynamic query with Linq

You can compose the expression (any Expression) manually from pieces and then append it to the previous part of query. You can find more info, with example in "Sorting in IQueryable using string as column name".

Related

Dynamic Linq on DataTable error: no Field or Property in DataRow, c#

I have some errors using Linq on DataTable and I couldn't figure it out how to solve it. I have to admit that i am pretty new to Linq and I searched the forum and Internet and couldn't figure it out. hope you can help.
I have a DataTable called campaign with three columns: ID (int), Product (string), Channel (string). The DataTable is already filled with data. I am trying to select a subset of the campaign records which satisfied the conditions selected by the end user. For example, the user want to list only if the Product is either 'EWH' or 'HEC'. The selection criteria is dynaically determined by the end user.
I have the following C# code:
private void btnClick()
{
IEnumerable<DataRow> query =
from zz in campaign.AsEnumerable()
orderby zz.Field<string>("ID")
select zz;
string whereClause = "zz.Field<string>(\"Product\") in ('EWH','HEC')";
query = query.Where(whereClause);
DataTable sublist = query.CopyToDataTable<DataRow>();
}
But it gives me an error on line: query = query.Where(whereClause), saying
No property or field 'zz' exists in type 'DataRow'".
If I changed to:
string whereClause = "Product in ('EWH','HEC')"; it will say:
No property or field 'Product' exists in type 'DataRow'
Can anyone help me on how to solve this problem? I feel it could be a pretty simple syntax change, but I just don't know at this time.
First, this line has an error
orderby zz.Field<string>("ID")
because as you said, your ID column is of type int.
Second, you need to learn LINQ query syntax. Forget about strings, the same way you used from, orderby, select in the query, you can also use where and many other operators. Also you'll need to learn the equivalent LINQ constructs for SQL-ish things, like for instance IN (...) is mapped to Enumerable.Contains etc.
With all that being said, here is your query
var productFilter = new[] { "EWH", "HEC" };
var query =
from zz in campaign.AsEnumerable()
where productFilter.Contains(zz.Field<string>("Product"))
orderby zz.Field<int>("ID")
select zz;
Update As per your comment, if you want to make this dynamic, then you need to switch to lambda syntax. Multiple and criteria can be composed by chaining multiple Where clauses like this
List<string> productFilter = ...; // coming from outside
List<string> channelFilter = ...; // coming from outside
var query = campaign.AsEnumerable();
// Apply filters if needed
if (productFilter != null && productFilter.Count > 0)
query = query.Where(zz => productFilter.Contains(zz.Field<string>("Product")));
if (channelFilter != null && channelFilter.Count > 0)
query = query.Where(zz => channelFilter.Contains(zz.Field<string>("Channel")));
// Once finished with filtering, do the ordering
query = query.OrderBy(zz => zz.Field<int>("ID"));

dynamic asc desc sort

I am trying to create table headers that sort during a back end call in nhibernate. When clicking the header it sends a string indicating what to sort by (ie "Name", "NameDesc") and sending it to the db call.
The db can get quite large so I also have back end filters and pagination built into reduce the size of the retrieved data and therefore the orderby needs to happen before or at the same time as the filters and skip and take to avoid ordering the smaller data. Here is an example of the QueryOver call:
IList<Event> s =
session.QueryOver<Event>(() => #eventAlias)
.Fetch(#event => #event.FiscalYear).Eager
.JoinQueryOver(() => #eventAlias.FiscalYear, () => fyAlias, JoinType.InnerJoin, Restrictions.On(() => fyAlias.Id).IsIn(_years))
.Where(() => !#eventAlias.IsDeleted);
.OrderBy(() => fyAlias.RefCode).Asc
.ThenBy(() => #eventAlias.Name).Asc
.Skip(numberOfRecordsToSkip)
.Take(numberOfRecordsInPage)
.List();
How can I accomplish this?
One way how to achieve this (one of many, because you can also use some fully-typed filter object etc or some query builder) could be like this draft:
Part one and two:
// I. a reference to our query
var query = session.QueryOver<Event>(() => #eventAlias);
// II. join, filter... whatever needed
query
.Fetch(#event => #event.FiscalYear).Eager
var joinQuery = query
.JoinQueryOver(...)
.Where(() => !#eventAlias.IsDeleted)
...
Part three:
// III. Order BY
// Assume we have a list of strings (passed from a UI client)
// here represented by these two values
var sortBy = new List<string> {"Name", "CodeDesc"};
// first, have a reference for the OrderBuilder
IQueryOverOrderBuilder<Event, Event> order = null;
// iterate the list
foreach (var sortProperty in sortBy)
{
// use Desc or Asc?
var useDesc = sortProperty.EndsWith("Desc");
// Clean the property name
var name = useDesc
? sortProperty.Remove(sortProperty.Length - 4, 4)
: sortProperty;
// Build the ORDER
order = order == null
? query.OrderBy(Projections.Property(name))
: query.ThenBy(Projections.Property(name))
;
// use DESC or ASC
query = useDesc ? order.Desc : order.Asc;
}
Finally the results:
// IV. back to query... call the DB and get the result
IList<Event> s = query
.List<Event>();
This draft is ready to do sorting on top of the root query. You can also extend that to be able to add some order statements to joinQuery (e.g. if the string is "FiscalYear.MonthDesc"). The logic would be similar, but built around the joinQuery (see at the part one)

How to get a list of the grouped values in linq groupby?

Linq newbie here, struggling with my first GroupBy query.
I have a list of objects of type KeywordInstance which represents a keyword, and the ID of the database record to which the keyword was applied.
Keyword RecordID
macrophages 1
macrophages 2
cell cycle 3
map kinase 2
cell cycle 1
What I want is a collection of all keywords, with a list of the RecordIDs to which each keyword was applied.
Keyword RecordIDs
macrophages 1, 2
cell cycle 1, 3
map kinase 2
I tried using Linq to get it into a new object. I only managed to get the distinct keywords.
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new {keyword = g.Key});
The problem is that I can't seem to get the values of g in any way. g is of the type IGrouping<String, KeywordInstance> and by documentation, it only has the property Key, but not even the property Value. All the examples I have seen on the Internet for groupby just tell me to select g itself, but the result of
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new {keyword = g.Key, RecordIDs = g});
is not what I want.
Any try to get something out of g fails with the error message System.Linq.IGropuing<string, UserQuery.KeywordInstance> does not have a definition for [whatever I tried].
What am I doing wrong here?
I think you are close to you solution.
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new
{
keyword = g.Key,
RecordIDs = g.Select(c => c.ID)
});
Just Select the records you need.
The reason you are seeing the Keyword-column as well as the ID-column, is becuase it's part of g
var keywords = allWords.GroupBy(w => w.keyword);
foreach (var itm in keywords)
{
var list = itm.ToList();
//list returns all of the original properties/values objects from allwords.
//itm.key returns w.keyword
}

Error getting items from Entity Framework using Lambda query

I have a listbox that I am trying to populate with the result of a SQL Server query via a Entity Framework linq/lambda query. I am feeding the query with a value from a combobox. I keep getting lot of errors like the following: Unable to create a constant value of type 'System.Object'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Any suggestions on how to fix this? I just want two fields to populate in a grid
var pAt = ent.Patterns.Where(p => p.Case_Id == (cbCase.SelectedItem as Case).Case_Id).Select(x => new Pattern{ PatternID = x.PatternID, Pattern1 = x.Pattern1 });
listBox1.DataSource = pAt;
listBox1.ValueMember = "PatternID";
listBox1.DisplayMember = "Pattern1";
Try this instead :
var pAt = ent.Patterns.AsEnumerable()
.Where(p => p.Case_Id == ((Case)cbCase.SelectedItem).Case_Id)
.Select(x => new Pattern{ PatternID = x.PatternID, Pattern1 = x.Pattern1 });
Hope this will fix your issue.
Separate the code parts from the SQL parts. Entity Framework can't necessarily construct an SQL query using code objects, but you can usually work around it. Eg:
var caseId = (cbCase.SelectedItem as Case).Case_Id;
var pAt = ent.Patterns.Where(p => p.Case_Id == caseId)
.ToArray()
.Select(x => new Pattern { PatternID = x.PatternID, Pattern1 = x.Pattern1 });

Building Dynamic LINQ Queries based on Combobox Value

I have a combo box in Silverlight. It has a collection of values built out of the properties of one of my LINQ-to-SQL objects (ie Name, Address, Age, etc...). I would like to filter my results based off the value selected in a combo box.
Example: Say I want everyone with a last name "Smith". I'd select 'Last Name' from the drop down list and enter smith into a textbox control. Normally I would write a LINQ query similar to...
var query = from p in collection where p.LastName == textbox.Text select p;
Is it possible to decide the property dynamically, maybe using Reflection? Something like
var query = from p in collection where p.(DropDownValue) == textbox.Text select p;
Assuming:
public class Person
{
public string LastName { get; set; }
}
IQueryable<Person> collection;
your query:
var query =
from p in collection
where p.LastName == textBox.Text
select p;
means the same as:
var query = collection.Where(p => p.LastName == textBox.Text);
which the compiler translates from an extension method to:
var query = Queryable.Where(collection, p => p.LastName == textBox.Text);
The second parameter of Queryable.Where is an Expression<Func<Person, bool>>. The compiler understands the Expression<> type and generates code to build an expression tree representing the lambda:
using System.Linq.Expressions;
var query = Queryable.Where(
collection,
Expression.Lambda<Func<Person, bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
Expression.Parameter(typeof(Person), "p"),
typeof(Person).GetProperty("LastName")),
Expression.MakeMemberAccess(
Expression.Constant(textBox),
typeof(TextBox).GetProperty("Text"))),
Expression.Parameter(typeof(Person), "p"));
That is what the query syntax means.
You are free to call these methods yourself. To change the compared property, replace this:
typeof(Person).GetProperty("LastName")
with:
typeof(Person).GetProperty(dropDown.SelectedValue);
Scott Guthrie has a short series on dyamically built LINQ to SQL queries:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
That's the easy way...then there's another way that's a bit more involved:
http://www.albahari.com/nutshell/predicatebuilder.aspx
You can also use the library I created: http://tomasp.net/blog/dynamic-linq-queries.aspx. You would store the properties in ComboBox as lambda expressions and then just write:
var f = (Expression<Func<Product, string>>)comboBox.SelectedValue;
var query =
from p in collection
where f.Expand(textBox.Text)
select p;

Resources