How can I use custom expressions in DevArt LINQ to Entities and also use query comprehension syntax? - linq

I've got a situation where I need to use a custom expression in a LINQ to Entities query (because I want to have custom logic that L2E wouldn't otherwise understand:
var query = db.MyTable.Where(MyPredicateExpression)
But I'd rather use query comprehension syntax:
var query = from x in db.MyTable where [x matches the predicate of MyPredicateExpression]
I know this is possible, because L2E supports it in other places:
var query = from x in db.MyTable where x.Length > 10
How do they make that work?
Edit: I'm using devart's LinqConnect for Oracle, which may behave somewhat differently than Microsoft L2E.

Entity Framework and LINQ to SQL do not support this scenario, because the translation of MyPredicateExpression should be added to expression tree translator.
I recommend you to create a stored function performing the predicate check and add this function to DataContext. You will be able to use a query like the following in this case:
var query = from x in db.MyTable where context.MyPredicateFunction(x.Field) select x;
Update. Here is the updated query that takes into account your comments:
int[] values = new int[] { 1, 2, 3 };
var query = from x in db.MyTable where values.Contains(x.AuditState) select x;
Update 2. You can add a Queryable property to your context that will be obtaining the necessary set of MyTable objects as shown in the following example:
public partial class MyDataContext {
IQueryable<MyTable> GetSpecialTables {
get {
int[] values = new int[] { 1, 2, 3 };
return this.MyTables.Where(x => values.Contains(x.AuditState));
}
}
}
Replace MyDataContext with the actual name of your context.

If I understand the problem correctly, you can either use an extension method OR call a function that returns a bool.

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

Can this Aggregate Lambda expression be converted to a LINQ query?

I have a list of integers summed by an Aggregate method using a Lambda expression:
var mylist = new int[] { 3, 4, 5 };
var result = mylist.Aggregate((a, b) => a + b);
As I understand it, a Lambda expression can always be converted to a LINQ query. How would such a LINQ query look for my example?
EDIT: I understand .Sum may be better to add the numbers in my example. But I would really like to know how this Aggregate will look with a LINQ Query instead.
It already IS a LINQ query, Aggregate is a LINQ operator, i'm assuming what you meant was how it would look like in the LINQ comprehension syntax? The comprehension syntax only has a few built in features (select , where, multiple selects, groupby etc), it doesn't have all operators built in so when you need one of those (such as aggregate) you wrap it around parenthèses and keep going with the regular syntax. Since there is nothing there except aggregate it's not possible to give an example so i'll go from a different query:
var mylist = new int[] { 3, 4, 5, 6, 7, 8 };
var result = mylist
.Where(item=>item %2 == 0)
.Aggregate((a, b) => a + b);
var ComprehensiveResult =
(from item in mylist
where item % 2 == 0
select item)
.Aggregate((a, b) => a + b);
Comprehensive syntax is more of a "LINQ for people coming from SQL introduction", there's nothing you can do in it that you can't do with plain using the operators but the reverse isn't true as not all operators have built in replacements. The only thing that comes to mind where Comprehensive syntax is better (aside from personal taste) is multiple selects to generate a cartesian product which is much harder to maintain in plain method syntax.
In this case Aggregate function adds numbers each other. So, the equivalent function is SUM:
var qry = mylist.Sum(x=>x);
or
var qry = (from n in mylist select n).Sum();
[EDIT]
OP has added extra information to the question without informing me about that.
Yes, it's possible to "convert" Aggregate function into linq query, but extension method is needed. See this article: Cumulating values with LINQ

Avoid "Parameterless Queries Error" with Linq Let clause

I'd like my Linq query to create an additional column in the results on the fly. In this case the column is a Class object I created that will contain image info. I was wondering what the right way is of doing this:
var validPics = (from x in db.picsVotesTagsJs let picObj = new CarShowImages(x.picname) where x.enabled == 1 select x).Take(25);
var myArray = validPicSummaries.ToArray();
Line 2 gerenates the error:
Only parameterless constructors and initializers are supported in LINQ to Entities.
This is my first time using the Let clause. My queries are usually pretty simple.
Create parameterless constructor and use some public property (e.g. PicName) to set picture name to your CarShowImages object:
var validPics = (from x in db.picsVotesTagsJs
where x.enabled == 1
select new CarShowImages { PicName = x.picname }).Take(25);
var myArray = validPics.ToArray();

Equivalent to SQL IN clause

I've got an entity called new_trexmail with a string attribute called new_contextline.
I'm trying to get a list of entities where new_contextlineis in a defined list.
The following code fails with the error : NotSupportedException: Invalid 'where' condition. An entity member is invoking an invalid property or method.
string[] test = new[]{"aaa", "hhh"};
var query = from n in New_trexmailSet
where test.Contains(n.New_contextline)
select n;
I understand why this error is being thrown but I'm wondering if it's possible to do the equiavalent of an IN clause using XRM.
If it is possible then how do I go about getting XRM to execute SELECT * FROM new_trexmail WHERE new_contextline in ('aaa', 'hhh')?
Thanks,
David
Check out the (longer than desired) list of LINQ limitations, particularly the limitation on the where clause:
The left side of the clause must be an attribute name and the right
side of the clause must be a value. You cannot set the left side to a
constant. Both the sides of the clause cannot be constants. Supports
the String functions Contains, StartsWith, EndsWith, and Equals.
So since test isn't a CRM attribute, you can't call Contains on it. However, one way around this is to use "Dynamic Linq" as developed by ScottGu and as demonstrated below:
//must include the below using statements
//using System.Linq;
//using System.Linq.Dynamic;
var trexmailSet = New_trexmailSet;
string[] test = new[] { "aaa", "hhh" };
string whereClause = "";
foreach (string name in test)
{
whereClause += string.Format("new_contextline = \"{0}\" OR ", name);
}
trexmailSet = trexmailSet.Where(whereClause.Substring(0, whereClause.Length - 4));
var query = from n in trexmailSet
select n;

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