evaluate column name in linq where clause - linq

My apologies if I'm missing something obvious here....
I'm trying to customize a method to create a RadComboBox filter that adjusts as a user types (based on a Telerik demo). I'm using a Business Logic layer to pull in my datasource, and then I'm trying to use linq to select the values for the combo box OnItemsRequested depending on which combo box has made the request. I'm trying to have set the parameters in the "where" clause based on which GridColumn filter is making the request.
Here's my code to fill the list:
private void list_ItemsRequested(object o, RadComboBoxItemsRequestedEventArgs e)
{
((RadComboBox)o).DataTextField = this.DataField;
((RadComboBox)o).DataValueField = this.DataField;
var employees = from emp in EmployeeBL.GetAllEmployees()
where emp.(this.UniqueName).Contains(e.Text)
select emp;
((RadComboBox)o).DataSource = employees;
((RadComboBox)o).DataBind();
}
Do I need to cast the UniqueName as parameter in my Data Object (EmployeeDTO)?
Thanks.
UPDATE::
Thanks to feedback, I've had some success with populating the combobox list. However, I think I've still got a miscue in my linq statement. The list builds the first time around, however, when I try to do the "StartsWith" comparison, the page throws an error saying the datasource contains no datarows, even though I'm definitely typing a "findable" string.
Here's what I have now.
private void list_ItemsRequested(RadComboBox o, RadComboBoxItemsRequestedEventArgs e)
{
o.DataTextField = this.DataField;
o.DataValueField = this.DataField;
DataTable dt = EmployeeBL.GetAllEmployees().AsDataTable();
IEnumerable<DataRow> query =
from emp in dt.AsEnumerable()
where emp.Field<String>(this.UniqueName).StartsWith(e.Text)
select emp;
DataTable boundTable = query.CopyToDataTable<DataRow>();
o.DataSource = boundTable;
o.DataBind();
}

There's not a built-in way. You have some choices:
Use a Dynamic Linq query library like ScottGu's
Use reflection to build an Expression from the property name
Use a switch statement to select an expression from a known list of properties (easy to code, less dynamic)
Use the CopyToDataTable extension method to create a data table that does support string-based sorting/filtering through DataViews

Are you trying to call the method named in this.UniqueName on each employee, and see if the result contains the text? Is so, you can use reflection.
If you're certain that o is a RadComboBox, it might as well be passed in as such.
private void list_ItemsRequested(RadComboBox o, RadComboBoxItemsRequestedEventArgs e)
{
o.DataTextField = this.DataField;
o.DataValueField = this.DataField;
PropertyInfo property = typeof(EmployeeDTO).GetProperty(this.UniqueName);
var employees = from emp in EmployeeBL.GetAllEmployees()
where ((IQueryable<string>)(property.GetValue(emp))).Contains(e.Text)
select emp;
o.DataSource = employees;
o.DataBind();
}

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"));

LINQ Select from dynamic tableName string

I want to get list of records from an entity model (I'm using EF version 5) with a particular accountID. I'm being supplied with the tableName string (this has to be dynamic) and the accountID. I'm trying the following 2 methods but none of them is working (giving me errors on the IQueryable object 'table':
PropertyInfo info = _db.GetType().GetProperty(tableName);
IQueryable table = info.GetValue(_db, null) as IQueryable;
var query = table.Where(t => t.AccountID == accID)
.Select(t => t);
List <object> recList = ( from records in table
where records.AccountID == accID
select records).ToList<object>();
The var query = table.Where(....).Select(...) is the correct move as it allows reflection for the query builder at runtime. However, t.AccountID is an error because of the type of t remains unknown.
I've previously used a similar approach in LINQ to SQL, using System.Linq.Expressions.Expression, e.g.:
// NOT TESTED
var table=context.GetTable(dynamicTableName);
var theT=table.Experssion; // actually, I forget. DynamicExpression or MemberBinding? or
var theField=Expression.Field(theT, "AccountID"); // or dynamic name
var query=table.Where(Expression.Equal(theField, accID);
var recList=query.ToList<object>();
If your object has a common interface there is a simpler syntax:
IQueryable<MyInterface> table = context.GetTable("table") as IQueryable<MyInterface>;
var recList=from r in table
where table.AccountID == ac // if your AccountID is on MyInterface
select table;
If you only have a few tables to support, you could do this as well:
IQueryable<MyInterface> table;
if("table1"==tableName)
table=_db.table1
elseif("table2"==tableName)
table=_db.table2
elseif("table3"==tableName)
table=_db.table3
else
throw exception
I built a DynamicRepository for a project I am working on. It uses generic methods exposed through EF along with dynamic linq. It might be helpful to look at that source code here:
https://dynamicmvc.codeplex.com/SourceControl/latest#DynamicMVC/DynamicMVC/Data/DynamicRepository.cs
You can query the entity framework metadata workspace to get the type for a given table name. This link might help:
Get Tables and Relationships

LINQ Select with multiple tables fields being writeable

I'm new to LINQ and I'm doing pretty well until now, but now stuck with this.
I've a LINQ object bounded to a DataGridView to let the user edit is contains.
for simple one table query, it go fine, but how to build a LINQ query with multiple table, so the result will still be read/write?
Here a example of what I mean:
GMR.Data.GMR_Entities GMR = new GMR.Data.GMR_Entities();
var dt = from Msg in GMR.tblMessages
join lang in GMR.tblDomVals on 1 equals 1//on Msg.pLangueID equals lang.ID
select Msg;
// select new {lang.DescrFr, Msg.Message,Msg.pLangueID } ;
this.dataGridView1.DataSource = dt;
In this simple query, if I return only "Msg" with the select statement, the grid can be edited. But if I replace the select statement with select new {lang.DescrFr, Msg.Message,Msg.pLangueID } ; the grid will be readable only.
I can easily understand that this is due because the query result is a anonymous type.
But is there a way to let the table tblMessage being writable?
try creating your own class, for example
public class MsgLangInfo
{
public string langDescFr{get;set;}
public int pLangueID{get;set;}
}
And at the select statement create an object of this class with new like below
select new MsgLangInfo {
langDescFr = lang.DescrFr,
langDescFr = Msg.Message,Msg.pLangueID
} ;
This way you can avoid the anonymous type problem.
You need to select the originals rows and explicitly set the grid columns.

Binding LINQ query to DataGridView

This is very confusing, I use AsDataView to bind query result to a dgv and it works fine with the following:
var query = from c in myDatabaseDataSet.Diamond where c.p_Id == p_Id select c;
dataGridView1.DataSource = query.AsDataView();
However, this one results in an Error:
var query = from item in myDatabaseDataSet.Items
where item.p_Id == p_Id
join diamond in myDatabaseDataSet.Diamond
on item.p_Id equals diamond.p_Id
join category in myDatabaseDataSet.DiamondCategory
on diamond.dc_Id equals category.dc_Id
select new
{
Product = item.p_Name,
Weight = diamond.d_Weight,
Category = category.dc_Name
};
dataGridView1.DataSource = query.AsDataView();
Error:
Instance argument: cannot convert from
'System.Collections.Generic.IEnumerable<AnonymousType#1>' to
'System.Data.DataTable'
AsDataView doesn't show up in query.(List). Why is this happen? How to bind the query above to the dgv then?.
The signature of the AsDataView is as follows:
public static DataView AsDataView(
this DataTable table
)
The only parameter is the DataTable.
The query you have is returning an IEnumerable of an anonymous type which doesn't have an implicit conversion to a DataTable (or a sequence of DataRow instances, in which case you could use that to help you create a DataTable).
You need to get the results back into a DataTable or something you can convert into a DataTable and then it will work.
In your particular case, it seems that you were (or are) using typed DataSets. If that is the case, then you should be able to take the values that were selected and then create new typed DataRow instances (there should be factory methods for you) which can then be put into a typed DataTable, which AsDataView can be called on.
just simply convert the result to a list and bind it to your grid.
var query = from item in myDatabaseDataSet.Items
where item.p_Id == p_Id
join diamond in myDatabaseDataSet.Diamond
on item.p_Id equals diamond.p_Id
join category in myDatabaseDataSet.DiamondCategory
on diamond.dc_Id equals category.dc_Id
select new
{
Product = item.p_Name,
Weight = diamond.d_Weight,
Category = category.dc_Name
}.ToList();
dataGridView1.DataSource = query;

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