Currently I am using expression builder for dynamic query generation.
I have created dynamic expressions for int, date time, and string operators. Now I am stuck at one point .
I want to compare month part of datetime through expression.
We are passing property of datetime and want to create expression for month of that property.
Code:
public static Expression GetDynamicquery<TSource>(
string property, object value, ParameterExpression p)
{
var propertyReference = Expression.Property(p, property);
var constantReference = Expression.Constant(value);
var expression = Expression.Equal(propertyReference, constantReference);
MethodInfo method = null;
return Expression.LessThanOrEqual(propertyReference, constantReference);
}
Here property is name of property which I am passing into Tsource.
p is parameter expression of type Tsource.
I want result like all birthdate of this month.
You can use
Expression.PropertyOrField method for that.
Not sure it would work with entity framework query though.
http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.propertyorfield(v=vs.110).aspx
Update
For entity framework you can use SqlFunctions.DatePart method.
http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions(v=vs.110).aspx
You would need an Expression.Call to represent that as an expression.
http://msdn.microsoft.com/en-us/library/bb349020(v=vs.110).aspx
I got answer of my question. What I did is like....
Code:
public static Expression GetDynamicquery<TSource>(
string property, object value, ParameterExpression p)
{
var propertyReference = Expression.Property(p, property);
propertyReference = Expression.Property(propertyReference, "Year");
var constantReference = Expression.Constant(value);
return Expression.Equal(propertyReference, constantReference);
}
Here first property reference will give datetime expression.
And again using that expression we can go one step inside and can access year property.
Same thing we can do for month.
Related
I tried to use Distinct() to filter my collection to prevent duplication but my linq query still adds the same values to list.
thanks in advance.
public ObservableCollection<string> CollectTopicsFromXml()
{
ObservableCollection<string> oc = new ObservableCollection<string>();
XDocument xDoc = XDocument.Load(path);
var topicColl = xDoc.Descendants("topic").Distinct();
foreach (var topic in topicColl)
{
oc.Add(topic.Value);
}
return oc;
}
Distinct by default uses reference equality unless Equals (and GetHashCode) are overridden on the item type. Since Equals is not overridden for XElement each element is "distinct" regardless of its contents.
If you want distinct elements by Name or some other property (or combination of properties) you have a few options:
Project the elements to an anonymous type which does implement value equality by default:
var topicColl = xDoc.Descendants("topic")
.Select(e => new {e.Name, e.Value})
.Distinct();
Use GroupBy, which allows an expression to be passed in
Create a class that implements IEqualityComparer<XElement> in the way that you want and pass that to Distinct
Use DistinctBy from MoreLinq which also allows an equality expression to be passed in
I am trying to send a Linq query as a string to a method to be used in a where clause. Since IEnumerable wouldn't work for this, I have converted my IEnumerable to IQueryable and still it throws error. The following is the code:
public static void FilterData(string Query)
{
if((List<MemberMaintenanceData>)HttpContext.Current.Session["Allmembers"] != null)
{
//Get the IEnumerable object colection from session
var data = (List<MemberMaintenanceData>) HttpContext.Current.Session["Allmembers"];
//Convert it to IQueryable
IQueryable<MemberMaintenanceData> queryData = data.AsQueryable();
//This line doesn't compile!!
queryData = queryData.Where(Query);
HttpContext.Current.Session["Allmembers"] = queryData.AsEnumerable().ToList();
}
}
I intended passing "a => a.AccountId == 1000" as Query
There is a free (and open source) library, provided by Microsoft for parsing strings into Lambda expressions that can then be used in Linq queries. It also contains versions of the standard query operators such as Where() that take a string parameter. You can find it described in Scott Guthries blog post on Dynamic Linq.
For example, you can do queries like this (adapted from a snippet from the Scott guthrie link)
// imagine these have come from a drop down box or some other user input...
string thingToSelectBy = "City";
string citySelectedByUser = "London";
int minNumberOfOrders = 10;
string whereClause = String.Format("{0} = #0 and Orders.Count >= #1", thingToSelectBy);
var query = db.Customers
.Where(whereClause, citySelectedByUser, minNumberOfOrders)
.OrderBy("CompanyName")
.Select("new(CompanyName as Name, Phone");
The Where clause in thisw code snippet shows how you create a where clause using a parameterised string and then dynamically inject values for the parameters at run time, for example, based on user input. This works for parameters of any type.
In your example, the where clause would be
whereClause = "AccountId = 1000";
So in effect you would be doing something like
var newFilteredQueryData = queryData.Where("AccountId = 1000");
That link also contains the location where you can download the source code and a comprehensive document describing the dynamic query API and expression language.
Given a class such as:
public class foo
{
public int AccountID {get;set;}
}
You should be able to do something like this:
Expression<Func<foo, bool>> filter = f => f.AccountID == 1000;
And then pass that as your query. If it is really needed as a string you can do this:
filter.ToString();
//By Using this library
using System.Linq.Dynamic.Core;
InventoryList = Repository.GetAll(); // IQueryable
string filterString = "UnitPrice > 10 And Qty>100 OR Description.Contains("Dairy")";
var filteredGenericList = InventoryList.Where(filterString);
I have a Linq (Entity Framework) Query as
function getData(string col_to_sort , bool IsAscending , int pageNo , int pageSize)
{
context.table_name.Skip(pageNo*pageSize).Take(pageSize).ToArray();
}
What i want is that if i pass the name of the column as a parameter to the function
and the order it will sort my query too.
Since my column name will be a string so we might need to convert it to ObjectQuery.
How can i achieve this?
Any help is appreciated
You can use Dynamic Linq:
string direction = IsAscending ? " ASC" : " DESC";
context.table_name.OrderBy(col_to_sort + direction).Skip(pageNo*pageSize).Take(pageSize).ToArray();
If you are using Dynamic Linq, then the accepted answer will work.
But If you don't want to add an extra library (Dynamic Linq), then you can pick my first approach. I will explain both the approaches where you have or don't have Dynamic Linq. You can select based on your preferences and choice.
First Approach: When you don't have Dynamic Linq:
If you are using using System.Linq; instead of using System.Linq.Dynamic.Core, then you can use this approach:
orderBy is the string and Student is the T (The Entity, in which we want to search).
Create a Utility class, something like this: (you can anytime covert to extension method If you wish.)
public static class LinqUtility
{
public static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
var propAsObject = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(propAsObject, parameter);
}
}
And you can use like this:
public async Task<IList<Student>> GetStudents(long groupId, string orderBy, Filter filter)
{
return await _context.Students.Where(x => x.StudentGroupId == groupId)
.OrderByDescending(LinqUtility.ToLambda<Student>(orderBy))
.Skip(filter.Skip)
.Take(filter.Take)
.ToListAsync();
}
Second Approach: When you have Dynamic Linq:
The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.
You need to include the Library separately. Include System.Linq.Dynamic.Core. The Author of this Library is not Microsoft.. As such no harm in using it.
So, this Library, provides you a method, that accepts the string.
You can pass comma separated values as well in string, this can be achieved by above code as well, but some changes would be required.
In the same example, just pass orderBy:
public async Task<IList<Student>> GetStudents(long groupId, string orderBy, Filter filter)
{
return await _context.Students.Where(x => x.StudentGroupId == groupId)
.OrderBy(orderBy)
.Skip(filter.Skip)
.Take(filter.Take)
.ToListAsync();
}
I know there is a way to use Expressions and Lambdas to accomplish this but I having a hard time piecing it all together. All I need is a method that will dynamically query an Entity Framework DBSet object to find the row where the propery with the given name matches the value.
My context:
public class MyContext : DbContext
{
public IDbSet<Account> Accoounts{ get { return Set<Account>(); } }
}
The method that I'm looking to write:
public T Get<T>(string property, object value) : where T is Account
{...}
I would rather not have to use Dynamic SQL to accomplish this so no need to suggest it because I already know it's possible. What I'm really looking for is some help to accomplish this using Expressions and Lambdas
Thanks in advance, I know it's brief but it should be pretty self-explanatory. Comment if more info is needed
I'm trying to avoid dynamic linq as much as possible because the main point of linq is strongly typed access. Using dynamic linq is a solution but it is exactly the oppose of the linq purpose and it is quite close to using ESQL and building the query from sting concatenation. Anyway dynamic linq is sometimes real time saver (especially when it comes to complex dynamic ordering) and I successfully use it in a large project with Linq-to-Sql.
What I usually do is defining some SearchCriteria class like:
public class SearchCriteria
{
public string Property1 { get; set; }
public int? Property2 { get; set; }
}
And helper query extension method like:
public static IQueryable<SomeClass> Filter(this IQueryable<SomeClass> query, SearchCriteria filter)
{
if (filter.Property1 != null) query = query.Where(s => s.Property1 == filter.Property1);
if (filter.Property2 != null) query = query.Where(s => s.Property2 == filter.Property2);
return query;
}
It is not generic solution. Again generic solution is for some strongly typed processing of classes sharing some behavior.
The more complex solution would be using predicate builder and build expression tree yourselves but again building expression tree is only more complex way to build ESQL query by concatenating strings.
Here's my implementation:
public T Get<T>(string property, object value) : where T is Account
{
//p
var p = Expression.Parameter(typeof(T));
//p.Property
var propertyExpression = Expression.Property(p, property);
//p.Property == value
var equalsExpression = Expression.Equal(propertyExpression, Expression.Constant(value));
//p => p.Property == value
var lambda = Expression.Lambda<Func<T,bool>>(equalsExpression, p);
return context.Set<T>().SingleOrDefault(lambda);
}
It uses EF 5's Set<T>() method. If you are using a lower version, you'll need to implement a way of getting the DbSet based on the <T> type.
Hope it helps.
Dynamic Linq may be an option. Specify your criteria as a string and it will get built as an expression and ran against your data;
An example from something I have done;
var context = new DataContext(ConfigurationManager.ConnectionStrings["c"].ConnectionString);
var statusConditions = "Status = 1";
var results = (IQueryable)context.Contacts.Where(statusConditions);
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I have IQueryable object and I need to take the data inside the IQueryable to put it into Textboxs controls. Is this possible?
I try something like:
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.????
}
Update:
I'm doing this:
public IQueryable getData(String tableName, Hashtable myparams)
{
decimal id = 0;
if (myparams.ContainsKey("id") == true)
id = (decimal)myparams["id"];
Type myType= Type.GetType("ORM_Linq." + tableName + ", ORM_Linq");
return this.GetTable(tableName , "select * from Articu where id_tipo_p = '" + id + "'");
}
public IQueryable<T> GetTable<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : class
{
return _datacontext.GetTable<T>().Where(predicate);
}
This returns a {System.Data.Linq.SqlClient.SqlProvider+OneTimeEnumerable1[ORM_Linq.Articu]}`
I don't see any method like you tell me. I see Cast<>, Expression, ToString...
EDIT: Updated based on additional info from your other posts...
Your getData method is returning IQueryable instead of a strongly typed result, which is why you end up casting it. Try changing it to:
public IQueryable<ORM_Linq.Articu> getData(...)
Are you trying to query for "Articu" from different tables?
With the above change in place, your code can be rewritten as follows:
ORM_Linq.Articu result = mydata.SingleOrDefault();
if (result != null)
{
TextBoxCode.Text = result.id.ToString();
TextBoxName.Text = result.descrip;
}
If you have a single result use SingleOrDefault which will return a default value if no results are returned:
var result = mydata.SingleOrDefault();
if (result != null)
{
textbox1.text = result.ProductName; // use the column name
}
else
{
// do something
}
If you have multiple results then loop over them:
foreach (var item in mydata)
{
string name = item.ProductName;
int id = item.ProductId;
// etc..
}
First, you should be using a strongly-typed version of IQueryable. Say that your objects are of type MyObject and that MyObject has a property called Name of type string. Then, first change the parameter mydata to be of type IQueryable<MyObject>:
public void setdata (IQueryable<MyObject> mydata)
Then we can write a body like so to actually get some data out of. Let's say that we just want the first result from the query:
public void setdata (IQueryable<MyObject> mydata) {
MyObject first = mydata.FirstOrDefault();
if(first != null) {
textbox1.Text = first.Name;
}
}
Or, if you want to concatenate all the names:
public void setdata(IQueryable<MyObject> mydata) {
string text = String.Join(", ", mydata.Select(x => x.Name).ToArray());
textbo1.Text = text;
}
Well, as the name suggests, an object implementing IQueryable is... Queryable! You'll need to write a linq query to get at the internal details of your IQueryable object. In your linq query you'll be able to pull out its data and assign bits of it where ever you'd like - like your text box.
Here's a great starting place for learning Linq.
I think you find the same mental struggle when coming from FoxPro and from DataSet. Really nice, powerful string-based capabilities(sql for query, access to tables and columns name) in these worlds are not available, but replaced with a compiled, strongly-typed set of capabilities.
This is very nice if you are statically defining the UI for search and results display against a data source known at compile time. Not so nice if you are trying to build a system which attaches to existing data sources known only at runtime and defined by configuration data.
If you expect only one value just call FirstOrDefault() method.
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.FirstOrDefault().PropertyName;
}