I have a dynamic select statement thus:
"new(PurchaseOrderID as ID_PK, PContractNo + GoodsSupplier.AssociatedTo.DisplayName as Search_Results)"
As can be seen I wish to concatenate the 'PContractNo' and 'GoodsSupplier.AssociatedTo.DisplayName' fields into one returned field named 'Search_Results'. It is important that these two fields are combined.
However the Linq library complains regarding the '+', which the expression parser brings back as a 'Concat(etc...), which of course cannot be translated into a store expression.
Obviously therefore I would like some help regarding just how i should format the select string in order to do what I want. I've tried many things!
Any help would be greatly appreciated!
Thank You, Ian Mac
Create a new Class like
public class A
{
public String k;
public String v;
}
and use linq to join
res = from a in list
select new A
{
k = a.Key,
v = String.Concat(a.Key,a.Value)
};
Related
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"));
I've been following with great interest the converstaion here:
Construct Query with Linq rather than SQL strings
with regards to constructing expression trees where even the table name is dynamic.
Toward that end, I've created a Extension method, addWhere, that looks like:
static public IQueryable<TResult> addWhere<TResult>(this IQueryable<TResult> query, string columnName, string value)
{
var providerType = query.Provider.GetType();
// Find the specific type parameter (the T in IQueryable<T>)
var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
var tableType = iqueryableT.GetGenericArguments()[0];
var tableName = tableType.Name;
var tableParam = Expression.Parameter(tableType, tableName);
var columnExpression = Expression.Equal(
Expression.Property(tableParam, columnName),
Expression.Constant(value));
var predicate = Expression.Lambda(columnExpression, tableParam);
var function = (Func<TResult, Boolean>)predicate.Compile();
var whereRes = query.Where(function);
var newquery = whereRes.AsQueryable();
return newquery;
}
[thanks to Timwi for the basis of that code]
Which functionally, works.
I can call:
query = query.addWhere("CurUnitType", "ML 15521.1");
and it's functionally equivalent to :
query = query.Where(l => l.CurUnitType.Equals("ML 15521.1"));
ie, the rows returned are the same.
However, I started watching the sql log, and I noticed with the line:
query = query.Where(l => l.CurUnitType.Equals("ML 15521.1"));
The Query generated is:
SELECT (A bunch of columns)
FROM [dbo].[ObjCurLocView] AS [t0]
WHERE [t0].[CurUnitType] = #p0
whereas when I use the line
query = query.addWhere("CurUnitType", "ML 15521.1");
The query generated is :
SELECT (the same bunch of columns)
FROM [dbo].[ObjCurLocView] AS [t0]
So, the comparison is now happening on the client side, instead of being added to the sql.
Obviously, this isn't so hot.
To be honest, I mostly cut-and-pasted the addWhere code from Timwi's (slightly different) example, so some of it is over my head. I'm wondering if there's any adjustment I can make to this code, so the expression is converted into the SQL statement, instead of being determined client-side
Thanks for taking the time to read through this, I welcome any comments, solutions, links, etc, that could help me with this. And of course if I find the solution through other means, I'll post the answer here.
Cheers.
The big problem is that you're converting the expression tree into a delegate. Look at the signature of Queryable.Where - it's expressed in expression trees, not delegates. So you're actually calling Enumerable.Where instead. That's why you need to call AsQueryable afterwards - but that doesn't do enough magic here. It doesn't really put it back into "just expression trees internally" land, because you've still got the delegate in there. It's now wrapped in an expression tree, but you've lost the details of what's going on inside.
I suspect what you want is this:
var predicate = Expression.Lambda<Func<TResult, Boolean>>
(columnExpression, tableParam);
return query.Where(predicate);
I readily admit that I haven't read the rest of your code, so there may be other things going on... but that's the core bit. You want a strongly typed expression tree (hence the call to the generic form of Expression.Lambda) which you can then pass into Queryable.Where. Give it a shot :)
How I can use Like query in LINQ ....
in sql for eg..
name like='apple';
thanks..
Use normal .NET methods. For example:
var query = from person in people
where person.Name.StartsWith("apple") // equivalent to LIKE 'apple%'
select person;
(Or EndsWith, or Contains.) LINQ to SQL will translate these into the appropriate SQL.
This will work in dot notation as well - there's nothing magic about query expressions:
// Will find New York
var query = cities.Where(city => city.Name.EndsWith("York"));
You need to use StartsWith, Contains or EndsWith depending on where your string can appear. For example:
var query = from c in ctx.Customers
where c.City.StartsWith("Lo")
select c;
will find all cities that start with "Lo" (e.g. London).
var query = from c in ctx.Customers
where c.City.Contains("York")
select c;
will find all cities that contain "York" (e.g. New York, Yorktown)
Source
name.contains("apple");
I use item.Contains("criteria"), but, it works efficiently only if you convert to lower both, criteria and item like this:
string criteria = txtSearchItemCriteria.Text.ToLower();
IEnumerable<Item> result = items.Where(x => x.Name.ToLower().Contains(criteria));
I have a linq Entity called Enquiry, which has a property: string DateSubmitted.
I'm writing an app where I need to return IQueryable for Enquiry that have a DateSubmitted within a particular date range.
Ideally I'd like to write something like
IQueryable<Enquiry> query = Context.EnquirySet.AsQueryable<Enquiry>();
int dateStart = int.Parse("20090729");
int dateEnd = int.Parse("20090930");
query = (from e in query
where(enq => int.Parse(enq.DateSubmitted) < dateEnd)
where(enq => int.Parse(enq.DateSubmitted) > dateStart)
select e);
Obviously Linq to EF doesn't recognise int.Parse, so I think I can achieve what I want with an Expression method that returns a predicate???
I've been playing around with PredicateBuilder and looking all over but I've successfully fried my brains trying to work this out. Sure I could add another property to my Entity and convert it there but I'd really like to understand this. Can anyone explain or give an example/link that doesn't fry my brains?
Thanks in advance
Mark
If you know your date strings are valid, and they're really in that order (which is a natural sort order) you might be able to get away with string comparisons:
IQueryable<Enquiry> query = Context.EnquirySet.AsQueryable<Enquiry>();
string dateStart ="20090729";
string dateEnd = "20090930";
query = (from e in query
where(enq => enq.DateSubmitted.CompareTo(dateEnd)) < 0)
where(enq => enq.DateSubmitted.CompareTo(dateStart)) > 0)
select e);
I have a need to replace multiple strings with others in a query
from p in dx.Table
where p.Field.Replace("A", "a").Replace("B", "b").ToLower() = SomeVar
select p
Which provides a nice single SQL statement with the relevant REPLACE() sql commands.
All good :)
I need to do this in a few queries around the application... So i'm looking for some help in this regard; that will work as above as a single SQL hit/command on the server
It seems from looking around i can't use RegEx as there is no SQL eq
Being a LINQ newbie is there a nice way for me to do this?
eg is it possible to get it as a IQueryable "var result" say and pass that to a function to add needed .Replace()'s and pass back? Can i get a quick example of how if so?
EDIT: This seems to work! does it look like it would be a problem?
var data = from p in dx.Videos select p;
data = AddReplacements(data, checkMediaItem);
theitem = data.FirstOrDefault();
...
public IQueryable<Video> AddReplacements(IQueryable<Video> DataSet, string checkMediaItem)
{
return DataSet.Where(p =>
p.Title.Replace(" ", "-").Replace("&", "-").Replace("?", "-") == checkMediaItem);
}
Wouldn't it be more performant to reverse what you are trying to do here, ie reformat the string you are checking against rather than reformatting the data in the database?
public IQueryable<Video> AddReplacements(IQueryable<Video> DataSet, string checkMediaItem)
{
var str = checkMediaItem.Replace("-", "?").Replace("&", "-").Replace("-", " "));
return DataSet.Where(p => p.Title == str);
}
Thus you are now comparing the field in the database with a set value, rather than scanning the table and transforming the data in each row and comparing that.