I write a extension. This extension a filtering mechanism is turning predicate object. But, I have a problem.
public static IQueryable<T> Filter<T>(this IQueryable<T> data, FilterGroupExpression filter)
{
var source = data;
Expression<Func<T, bool>> predicate;
if (filter.Operator == FilterGroupOperators.And)
predicate = PredicateBuilder.True<T>();
else
predicate = PredicateBuilder.False<T>();
foreach (FilterRuleDescriptive ruleExp in filter.Rules)
{
if (filter.Operator == FilterGroupOperators.And)
predicate = predicate.And<T>(MakeExpression<T>(ruleExp));
else
predicate = predicate.Or<T>(MakeExpression<T>(ruleExp));
}
foreach (FilterGroupExpression groupExp in filter.Groups)
{
if (filter.Operator == FilterGroupOperators.And)
predicate = predicate.And<T>(SubGroupFilter<T>(groupExp));
else
predicate = predicate.Or<T>(SubGroupFilter<T>(groupExp));
}
return source.Where(predicate);
}
public static Expression<Func<T, bool>> SubGroupFilter<T>(FilterGroupExpression filter)
{
Expression<Func<T, bool>> predicate;
if (filter.Operator == FilterGroupOperators.And)
predicate = PredicateBuilder.True<T>();
else
predicate = PredicateBuilder.False<T>();
foreach (FilterRuleDescriptive ruleExp in filter.Rules)
{
if (filter.Operator == FilterGroupOperators.And)
predicate = predicate.And<T>(MakeExpression<T>(ruleExp));
else
predicate = predicate.Or<T>(MakeExpression<T>(ruleExp));
}
foreach (FilterGroupExpression groupExp in filter.Groups)
{
predicate = SubGroupFilter<T>(groupExp);
}
return predicate;
}
public static Expression<Func<T, bool>> MakeExpression<T>(FilterRuleDescriptive condition)
{
Expression currentExpr = null;
var param = Expression.Parameter(typeof(T), "param");
var prop = Expression.Property(param, condition.Field);
var propType = Nullable.GetUnderlyingType(prop.Type) ?? prop.Type;
ConstantExpression constant = null;
condition.Value = Convert.ChangeType(condition.Value, propType);
if (IsNullableType(prop.Type))
constant = Expression.Constant(condition.Value.ConvertToNullable<int>(), prop.Type);
else
constant = Expression.Constant(condition.Value, propType);
if (condition.Value.GetType() == typeof(string))
condition.Value = condition.Value.ToString().Replace("'", string.Empty);
switch (condition.Operator)
{
case FilterRuleOperator.Equals:
currentExpr = Expression.Equal(prop, constant);
break;
case FilterRuleOperator.DoesNotEqual:
currentExpr = Expression.NotEqual(prop, constant);
break;
case FilterRuleOperator.StartsWith:
currentExpr = Expression.Call(prop, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }), constant);
break;
case FilterRuleOperator.EndsWith:
currentExpr = Expression.Call(prop, typeof(string).GetMethod("EndsWith", new[] { typeof(string) }), constant);
break;
case FilterRuleOperator.Contains:
currentExpr = Expression.Call(prop, typeof(string).GetMethod("Contains", new[] { typeof(string) }), constant);
break;
case FilterRuleOperator.IsGreaterThan:
currentExpr = Expression.GreaterThan(prop, constant);
break;
case FilterRuleOperator.IsGreaterThanOrEqual:
currentExpr = Expression.GreaterThanOrEqual(prop, constant);
break;
case FilterRuleOperator.IsLessThan:
currentExpr = Expression.LessThan(prop, constant);
break;
case FilterRuleOperator.IsLessThanOrEqual:
currentExpr = Expression.LessThanOrEqual(prop, constant);
break;
case FilterRuleOperator.IsNull:
currentExpr = Expression.Equal(prop, constant);
break;
case FilterRuleOperator.IsNotNull:
currentExpr = Expression.NotEqual(prop, constant);
break;
default:
throw new ArgumentOutOfRangeException();
}
return Expression.Lambda<Func<T, bool>>(currentExpr, param);
}
source.Where(predicate) returned:
SELECT [t0].[Id], [t0].[Name], [t0].[SmallDescription], [t0].[Description], [t0].[Barcode], [t0].[CategoryId], [t0].[Code], [t0].[PurchasePrice], [t0].[Price], [t0].[OldPrice], [t0].[VatRatio], [t0].[MetaDescription], [t0].[MetaKeywords], [t0].[VariantFirstTypeId], [t0].[VariantSecondTypeId], [t0].[MarkId], [t0].[Mark], [t0].[Stok], [t0].[ShowCase], [t0].[Firm], [t0].[IsVariant], [t0].[IsValid], [t0].[IsSale] FROM [dbo].[Product] AS [t0] WHERE ([t0].[Code] LIKE #p0) AND ([t0].[Name] LIKE #p1) AND ([t0].[Price] >= #p2) AND ([t0].[Price] < #p3) AND (([t0].[SmallDescription] LIKE #p4) OR ([t0].[SmallDescription] LIKE #p5))
No problems so far.
this generated SQL query, the MS SQL Query Editor turns 24 data when I say run. This is an accurate result.
But,
var result = source.Where(predicate);
When questioned by LinQ, 0 turns data.
result.Count() = 0
What could be the problem?
Help me please... :(
Related
I am new to Oracle in Visual Studio,
I used Oracle.ManagedDataAccess as my reference,
The case is whenever I tried to retrieve decimal value from arithmetic in Oracle query, Its always return null
e.g
SELECT 26/3 FROM DUAL < ---- This code return null in my visual studio but has a value in TOAD.
Did I do it wrong ?
Here is my code to retrieve the value
List<object[]> result = new List<object[]>();
OracleDataReader data;
string constr = ConfigurationManager.ConnectionStrings["OraConnection"].ConnectionString;
using (OracleConnection con = new OracleConnection(constr))
{
string query = QueryString;
using (OracleCommand cmd = new OracleCommand(query))
{
cmd.Connection = con;
con.Open();
data = cmd.ExecuteReader();
try
{
if (data.HasRows)
{
while (data.Read())
{
object[] itemData = new object[data.FieldCount];
//Dictionary<string, string> itemData = new Dictionary<string, string>();
for (int i = 0; i < data.FieldCount; i++)
{
Type type = data.GetValue(i).GetType();
if (typeof(string) == type)
{
itemData[i] = data.GetString(i);
}
if (typeof(DateTime) == type)
{
itemData[i] = data.GetDateTime(i);
}
if (typeof(int) == type)
{
itemData[i] = data.GetInt32(i);
}
if (typeof(decimal) == type)
{
itemData[i] = data.GetDecimal(i);
}
if (typeof(bool) == type)
{
itemData[i] = data.GetBoolean(i);
}
if (typeof(TimeSpan) == type)
{
itemData[i] = data.GetTimeSpan(i);
}
if (typeof(Single) == type)
{
itemData[i] = Convert.ToDecimal(data.GetOracleDecimal(i).ToString());
}
}
result.Add(itemData);
}
}
else
{
Console.WriteLine("Rows not found.");
}
}
finally
{
data.Close();
}
con.Close();
}
}
return result;
UPDATED : It got null just for division which has decimal value. Addition, Substraction, multiplication has no issue
It seems your data type doesn't match any of your if expressions. As there is no default branch, itemData[i] remains null. I suggest something like the following to find the gap:
for (int i = 0; i < data.FieldCount; i++)
{
Type type = data.GetValue(i).GetType();
switch(type)
{
case typeof(string):
itemData[i] = data.GetString(i);
break;
case typeof(DateTime):
itemData[i] = data.GetDateTime(i);
break;
case typeof(int):
itemData[i] = data.GetInt32(i);
break;
case typeof(decimal):
itemData[i] = data.GetDecimal(i);
break;
case typeof(bool):
itemData[i] = data.GetBoolean(i);
break;
case typeof(TimeSpan):
itemData[i] = data.GetTimeSpan(i);
break;
case typeof(Single):
itemData[i] = Convert.ToDecimal(data.GetOracleDecimal(i).ToString());
break;
default:
MessageBox.Show("Unknown type " + type.Name);
break;
}
}
So, I got an advice to edit the oracle query
From (e.g.)
SELECT 26/7 FROM DUAL
TO
SELECT TO_CHAR(26/7) FROM DUAL
And yes it works
But still, I don't know why
I just want to make Column Filter for grid view. Simple I just want to filter grid view column with some extra stuff. Here I have created one IQueryable that returns queryable result.
Here is my code :
------------------------------------------Updated------------------------------------------------
public static IQueryable<T> FilterForColumn<T>(this IQueryable<T> queryable, string colName, string searchText)
{
if (colName != null && searchText != null)
{
var parameter = Expression.Parameter(typeof(T), "m");
var propertyExpression = Expression.Property(parameter, colName);
System.Linq.Expressions.ConstantExpression searchExpression = null;
System.Reflection.MethodInfo containsMethod = null;
System.Linq.Expressions.MethodCallExpression body = null;
System.Nullable<DateTime> nextDate = null;
Expression ex1 = null;
Expression ex2 = null;
switch (colName)
{
case "JobID":
case "FileSize":
case "TotalFileSize":
Int64? size = Convert.ToInt64(searchText);
searchExpression = Expression.Constant(searchText);
containsMethod = typeof(Int64?).GetMethod("Equals", new[] { typeof(Int64?) });
body = Expression.Call(propertyExpression, containsMethod, searchExpression);
break;
case "PublishDate":
case "Birth_date":
case "Anniversary_date":
case "Profile_Updated_datetime":
case "CompletedOn":
DateTime? currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
nextDate = currentDate.Value.AddDays(1);
ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate));
ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate));
body = Expression.AndAlso(ex1, ex2);
break;
case "Created_datetime":
case "Reminder_Date":
case "News_date":
case "thought_date":
case "SubscriptionDateTime":
case "Register_datetime":
case "CreatedOn":
DateTime dt1 = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
nextDate = currentDate.Value.AddDays(1);
ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate));
ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate));
body = Expression.AndAlso(ex1, ex2);
break;
default :
searchExpression = Expression.Constant(searchText);
containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(propertyExpression, containsMethod, searchExpression);
break;
}
var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
return queryable.Where(predicate);
}
else
{
return queryable;
}
}
here this solution gives some compile time error :
Error 9 Cannot implicitly convert type 'System.Linq.Expressions.BinaryExpression' to 'System.Linq.Expressions.MethodCallExpression' F:\EasyWeb\App_Code\Helper.cs 47 28 F:\EasyWeb\
Here at case of DateTime I just want to filter DateTime column with only Date not whole DATETIME value.
Here I just put required things :
SELECT [t8].[Id], [t8].[Title], [t8].[value] AS [Publisher], [t8].[value2] AS [ToUser], [t8].[Sent_Datetime] AS [PublishDate], [t8].[IsFileAttached] AS [IsFile], [t8].[Category_name] AS [CategoryName], [t8].[value3] AS [status_name], [t8].[value4] AS [Group_name], [t8].[TotalFileSize] AS [FileSize]
FROM (
SELECT [t0].[Id], [t0].[Title], (
SELECT TOP (1) [t3].[value]
FROM (
SELECT ([t2].[First_name] + ' ') + [t2].[Last_name] AS [value], [t2].[Id]
FROM [dbo].[tbl_User_master] AS [t2]
) AS [t3]
WHERE [t3].[Id] = [t0].[User_id]
) AS [value], (
SELECT TOP (1) [t5].[value]
FROM (
SELECT ([t4].[First_name] + ' ') + [t4].[Last_name] AS [value], [t4].[Id]
FROM [dbo].[tbl_User_master] AS [t4]
) AS [t5]
WHERE ([t5].[Id]) = [t0].[ToUser_id]
) AS [value2], [t0].[Sent_Datetime], [t0].[IsFileAttached], [t1].[Category_name], (
SELECT TOP (1) [t6].[status_name]
FROM [dbo].[tbl_status_master] AS [t6]
WHERE ([t6].[Id]) = [t0].[status_id]
) AS [value3], (
SELECT TOP (1) [t7].[Group_name]
FROM [dbo].[tbl_Group_master] AS [t7]
WHERE ([t7].[Id]) = [t0].[group_id]
) AS [value4], [t0].[TotalFileSize], [t0].[ToUser_id], [t0].[User_id]
FROM [dbo].[tbl_Post_History] AS [t0]
INNER JOIN [dbo].[tbl_Category_master] AS [t1] ON [t0].[Category_id] = [t1].[Id]
) AS [t8]
WHERE (CAST(CONVERT(CHAR(10), [t8].[Sent_Datetime], 102) AS DATETIME) = '12/24/2013' ) AND (([t8].[ToUser_id] = 3) OR ([t8].[ToUser_id] IS NULL)) AND ([t8].[User_id] <> 3)
ORDER BY [t8].[Sent_Datetime] DESC
Here this shows required output or logic that we will do with queryable.
But, here one drawback occurs while this method Equals check whole Date Time.
Is that possible with this method to force only check Date Part from column?
------------------------------------2-Updated-----------------------------------------
for overwhelming this problem i use this technique :
public static IQueryable<T> FilterForColumn<T>(this IQueryable<T> queryable, string colName, string searchText)
{
if (colName != null && searchText != null)
{
var parameter = Expression.Parameter(typeof(T), "m");
var propertyExpression = Expression.Property(parameter, colName);
System.Linq.Expressions.ConstantExpression searchExpression = null;
System.Reflection.MethodInfo containsMethod = null;
System.Linq.Expressions.BinaryExpression body = null;
DateTime? currentDate = null;
DateTime? nextDate = null;
Expression ex1 = null;
Expression ex2 = null;
switch (colName)
{
case "JobID":
case "FileSize":
case "TotalFileSize":
Int64? size = Convert.ToInt64(searchText);
searchExpression = Expression.Constant(searchText);
containsMethod = typeof(Int64?).GetMethod("Equals", new[] { typeof(Int64?) });
//body = Expression.Call(propertyExpression, containsMethod, searchExpression);
break;
case "PublishDate":
case "Birth_date":
case "Anniversary_date":
case "Profile_Updated_datetime":
case "CompletedOn":
currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
nextDate = currentDate.Value.AddDays(1);
ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate));
ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate));
body = Expression.AndAlso(ex1, ex2);
break;
case "Created_datetime":
case "Reminder_Date":
case "News_date":
case "thought_date":
case "SubscriptionDateTime":
case "Register_datetime":
case "CreatedOn":
currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
nextDate = currentDate.Value.AddDays(1);
ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate));
ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate));
body = Expression.AndAlso(ex1, ex2);
break;
default :
searchExpression = Expression.Constant(searchText);
containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
//body = Expression.Call(propertyExpression, containsMethod, searchExpression);
break;
}
var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
return queryable.Where(predicate);
}
else
{
return queryable;
}
}
it's give me run time error like :
Server Error in '/EasyWeb' Application.
The binary operator GreaterThanOrEqual is not defined for the types 'System.Nullable`1[System.DateTime]' and 'System.DateTime'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The binary operator GreaterThanOrEqual is not defined for the types 'System.Nullable`1[System.DateTime]' and 'System.DateTime'.
Source Error:
Line 44: currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
Line 45: nextDate = currentDate.Value.AddDays(1);
Line 46: ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate));
Line 47: ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate));
Line 48: body = Expression.AndAlso(ex1, ex2);
Source File: f:\EasyWeb\App_Code\Helper.cs Line: 46
Stack Trace:
[InvalidOperationException: The binary operator GreaterThanOrEqual is not defined for the types 'System.Nullable`1[System.DateTime]' and 'System.DateTime'.]
System.Linq.Expressions.Expression.GetUserDefinedBinaryOperatorOrThrow(ExpressionType binaryType, String name, Expression left, Expression right, Boolean liftToNull) +752213
System.Linq.Expressions.Expression.GetComparisonOperator(ExpressionType binaryType, String opName, Expression left, Expression right, Boolean liftToNull) +221
System.Linq.Expressions.Expression.GreaterThanOrEqual(Expression left, Expression right, Boolean liftToNull, MethodInfo method) +53
System.Linq.Expressions.Expression.GreaterThanOrEqual(Expression left, Expression right) +8
Helper.FilterForColumn(IQueryable`1 queryable, String colName, String searchText) in f:\EasyWeb\App_Code\Helper.cs:46
Admin_Post_History.FillGrid(String CommandName, String ColumnName, String SearchText) in f:\EasyWeb\Admin\Post_History.aspx.cs:79
Admin_Post_History.btnsearch_Click(Object sender, EventArgs e) in f:\EasyWeb\Admin\Post_History.aspx.cs:2375
System.Web.UI.WebControls.Button.OnClick(EventArgs e) +111
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +110
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +36
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1565
Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053
-------------------------------- 3 Update-------------------------------------------
here is what i'm querying :
case "Inbox":
lbl_disply.Text = "Inbox";
lbut_showinbox.Font.Bold = true;
lbut_showoutbox.Font.Bold = false;
lbut_showdraffs.Font.Bold = false;
lbut_showtrash.Font.Bold = false;
var query1 = db.Posts.Where(p => (p.ToUser_id.Equals(user_id) || p.ToUser_id.Equals(null)) && p.User_id != user_id).OrderByDescending(p=>p.Sent_Datetime).Select(p => new
{
Id = p.Id,
Title = p.Title,
Publisher = db.Users.Where(u => u.Id.Equals(p.User_id)).Select(u => u.First_name + ' ' + u.Last_name).FirstOrDefault(),
ToUser = db.Users.Where(u => u.Id.Equals(p.ToUser_id)).Select(u => u.First_name + ' ' + u.Last_name).FirstOrDefault(),
PublishDate = p.Sent_Datetime,
IsFile = p.IsFileAttached,
CategoryName = db.Categories.Where(c => c.Id.Equals(p.Category_id)).Select(c => c.Category_name).FirstOrDefault(),
status_name = db.Status.Where(s => s.Id.Equals(p.status_id)).Select(s => s.status_name).FirstOrDefault(),
Group_name = db.Groups.Where(g => g.Id.Equals(p.group_id)).Select(g => g.Group_name).FirstOrDefault(),
FileSize = p.TotalFileSize
}).FilterForColumn(ColumnName, SearchText).ToList();
at grid view filling
DataSet myDataSet = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Id", typeof(int)));
dt.Columns.Add(new DataColumn("IsRead", typeof(bool)));
dt.Columns.Add(new DataColumn("IsImp", typeof(bool)));
dt.Columns.Add(new DataColumn("Title", typeof(string)));
dt.Columns.Add(new DataColumn("Publisher", typeof(string)));
dt.Columns.Add(new DataColumn("ToUser", typeof(string)));
dt.Columns.Add(new DataColumn("PublishDate", typeof(DateTime?)));
dt.Columns.Add(new DataColumn("IsFile", typeof(bool)));
dt.Columns.Add(new DataColumn("CategoryName", typeof(string)));
dt.Columns.Add(new DataColumn("status_name", typeof(string)));
dt.Columns.Add(new DataColumn("Group_name", typeof(string)));
dt.Columns.Add(new DataColumn("FileSize", typeof(string)));
foreach (var item in query1)
i declared this PublishDate as typeof(DateTime?) but this gives me run time error like :
Server Error in '/EasyWeb' Application.
DataSet does not support System.Nullable<>.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: DataSet does not support System.Nullable<>.
Source Error:
Line 101: dt.Columns.Add(new DataColumn("Publisher", typeof(string)));
Line 102: dt.Columns.Add(new DataColumn("ToUser", typeof(string)));
Line 103: dt.Columns.Add(new DataColumn("PublishDate", typeof(DateTime?)));
Line 104: dt.Columns.Add(new DataColumn("IsFile", typeof(bool)));
Line 105: dt.Columns.Add(new DataColumn("CategoryName", typeof(string)));
Source File: f:\EasyWeb\Admin\Post_History.aspx.cs Line: 103
Stack Trace:
[NotSupportedException: DataSet does not support System.Nullable<>.]
System.Data.DataColumn..ctor(String columnName, Type dataType, String expr, MappingType type) +4826536
System.Data.DataColumn..ctor(String columnName, Type dataType) +12
Admin_Post_History.FillGrid(String CommandName, String ColumnName, String SearchText) in f:\EasyWeb\Admin\Post_History.aspx.cs:103
Admin_Post_History.Page_Load(Object sender, EventArgs e) in f:\EasyWeb\Admin\Post_History.aspx.cs:59
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
System.Web.UI.Control.OnLoad(EventArgs e) +99
System.Web.UI.Control.LoadRecursive() +50
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627
Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053
---------------------------------4 Update----------------------------------------
i check my dbml file and this is screen shot for verify that it's nullable
To achieve what you want you can use predicate
WHERE Sent_Datetime >= '20131224' AND Sent_Datetime < '20131225'
so you can use this expression
DateTime currentDate = DateTime.ParseExact("06/01/2008", "dd/MM/yyyy", null);
DateTime nextDate = currentDate.AddDays(1);
Expression ex1 = Expression.GreaterThanOrEqual(
propertyExpression, Expression.Constant(currentDate));
Expression ex2 = Expression.LessThan(
propertyExpression, Expression.Constant(nextDate));
Expression body = Expression.AndAlso(ex1, ex2);
var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
Of course, here you have sargable predicate.
Update
I've created the complete example for you:
public static IQueryable<T> FilterForColumn<T>(this IQueryable<T> queryable, string colName, string searchText)
{
if (colName != null && searchText != null)
{
var parameter = Expression.Parameter(typeof(T), "m");
var propertyExpression = Expression.Property(parameter, colName);
System.Linq.Expressions.ConstantExpression searchExpression = null;
System.Reflection.MethodInfo containsMethod = null;
// this must be of type Expression to accept different type of expressions
// i.e. BinaryExpression, MethodCallExpression, ...
System.Linq.Expressions.Expression body = null;
Expression ex1 = null;
Expression ex2 = null;
switch (colName)
{
case "JobID":
case "FileSize":
case "TotalFileSize":
Int64? size = Convert.ToInt64(searchText);
searchExpression = Expression.Constant(searchText);
containsMethod = typeof(Int64?).GetMethod("Equals", new[] { typeof(Int64?) });
body = Expression.Call(propertyExpression, containsMethod, searchExpression);
break;
// section for DateTime? properties
case "PublishDate":
case "Birth_date":
case "Anniversary_date":
case "Profile_Updated_datetime":
case "CompletedOn":
DateTime currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
DateTime nextDate = currentDate.AddDays(1);
ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate, typeof(DateTime?)));
ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate, typeof(DateTime?)));
body = Expression.AndAlso(ex1, ex2);
break;
// section for DateTime properties
case "Created_datetime":
case "Reminder_Date":
case "News_date":
case "thought_date":
case "SubscriptionDateTime":
case "Register_datetime":
case "CreatedOn":
DateTime currentDate = DateTime.ParseExact(searchText, "dd/MM/yyyy", null);
DateTime nextDate = currentDate.AddDays(1);
ex1 = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(currentDate));
ex2 = Expression.LessThan(propertyExpression, Expression.Constant(nextDate));
body = Expression.AndAlso(ex1, ex2);
break;
default :
searchExpression = Expression.Constant(searchText);
containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(propertyExpression, containsMethod, searchExpression);
break;
}
var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
return queryable.Where(predicate);
}
else
{
return queryable;
}
}
You can use the SqlFunctions.DateDiff function specifying "day" value as datepart parameter.
var result = entities
.Where(item =>
SqlFunctions.DateDiff("day", item.DateProperty, dateToCompare) == 0);
I have a generic repository method to enable server side paging:
public virtual IEnumerable<T> GetList(out int totalPages, Expression<Func<T, bool>> filter = null,
Expression<Func<T, object>> orderby = null,
bool ascending = true,
string includeProperties = "",
int pageSize = 10, int pageNumber = 1)
{
IQueryable<T> query = c.Set<T>();
if (filter != null)
{
query = query.Where(filter);
}
query = includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProp) => current.Include(includeProp));
if (orderby != null)
{
query = #ascending ? query.OrderBy(#orderby) : query.OrderByDescending(#orderby);
}
else
{
query = #ascending ? query.OrderBy(o => o.Id) : query.OrderByDescending(o => o.Id);
}
//totalPages = (int) Math.Ceiling((double)(Queryable.Count(query) / pageSize));
totalPages = 1;
if (pageSize > 0)
{
var skip = 0;
var take = pageSize;
if (pageNumber > 1)
{
skip = (pageNumber - 1) * pageSize;
take = pageSize + skip;
}
query = query.Take(take);
query = query.Skip(skip);
}
return query.ToList();
}
T is a base class inherited by my entities
I call this repository method from my service. and i call my service from my controller.
Now from my controller how do i create my orderby expression so that i can pass any column name?
What i've tried so far (found on stackoverflow):
var _OrderByProperty = typeof(Year).GetProperty("Id");
var _OrderByParameter = Expression.Parameter(typeof(Year), "x");
var _OrderByBody = Expression.Property(_OrderByParameter, _OrderByProperty.Name);
var _OrderByConverted = Expression.Convert(_OrderByBody, typeof(Object));
var _OrderByLambda = Expression.Lambda<Func<Year, object>>
(_OrderByConverted, _OrderByParameter);
var list = s.GetList(orderby: _OrderByLambda, totalPages: out totalPages, pageNumber: pageNumber, pageSize: pageSize, ascending: isAsc);
i'm getting the error
Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
With linq I want to use order by with specific column but I need two switches because i don't know how to use desc or asc in one
public class CustomersRepository : RepositoryBase<Customers>
{
public List<Customers> GetAll(CustomersProperties property, SortEnum sortEnum, int page, int limit)
{
var query = _context.Set<Customers>();
switch (sortEnum)
{
case SortEnum.Ascending:
switch (property)
{
case CustomersProperties.Name:
query = query.OrderBy(x => x.Name);
break;
case CustomersProperties.Surname:
query = query.OrderBy(x => x.Lastname);
break;
default:
throw new ArgumentOutOfRangeException("property");
}
break;
case SortEnum.Descending:
break;
default:
throw new ArgumentOutOfRangeException("sortEnum");
}
return query.Skip(page * limit)
.Take(limit).ToList();
}
}
Is it possible to do without two switch cases?
Be aware that query.OrderBy(x => x.Name); does not do anything since the sorted collection is returned from OrderBy, and you're not capturing that return.
That said, there's not a way to "dynamically" choose the direction in Linq. However, a conditional switch would be a little cleaner. Another option would be to capture the sort expression in a variable:
Expreccion<Func<Customers, string>> propExp;
switch (property)
{
case CustomersProperties.Name:
propExp = ((Customers)x => x.Name)
break;
case CustomersProperties.Surname:
propExp = ((Customers)x => x.Lastname);
break;
default:
throw new ArgumentOutOfRangeException("property");
}
query = sortEnum == SortEnum.Ascending
? query.OrderBy(propExp);
: query.OrderByDescending(propExp);
You could make your own overload, something like this:
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector, SortEnum sort) {
switch (sort) {
case SortEnum.Ascending:
return source.OrderBy(keySelector);
case SortEnum.Descending:
return source.OrderByDescending(keySelector);
default:
throw new ArgumentOutOfRangeException("sort");
}
}
// later on..
query = query.OrderBy(x => x.LastName, sortEnum);
You can prepare sortProperty Expression first and then use it with either OrderBy or OrderByDescending:
public List<Customers> GetAll(CustomersProperties property, SortEnum sortEnum, int page, int limit)
{
var query = _context.Set<Customers>();
Expression<Func<Customers, string>> sortProperty;
switch (property)
{
case CustomersProperties.Name:
sortProperty = x => x.Name;
break;
case CustomersProperties.Surname:
sortProperty = x => x.Lastname;
break;
default:
throw new ArgumentOutOfRangeException("property");
}
switch (sortEnum)
{
case SortEnum.Ascending:
query = query.OrderBy(sortProperty);
break;
case SortEnum.Descending:
query = query.OrderByDescending(sortProperty);
break;
default:
throw new ArgumentOutOfRangeException("sortEnum");
}
return query.Skip(page * limit)
.Take(limit).ToList();
}
I have an application where all queries are created dynamically based on a simple data message received by a WCF service. A data message is, put simply, a collection of columnname/column value pairs, with the addition of an operator, e.g. Equals, Less Than, etc.
Simple Data Message of ColumnName-Value-Operator
Name, Joe, Equals
Age, 35, Less Than
Occupation, Geek, Equals
Rate, 1000, Greater Than
etc...
I have been somewhat successfully using dynamic binary expressions based on the contents of the datamessage.
BinaryExpression expression = null;
ParameterExpression parameter = Expression.Parameter(typeof(MessageType), "p");
foreach (row in DataMessage)
{
BinaryExpression exp = DataLib.MakeQueryFilter(typeof(MessageType),
row.ColumnName,row.ColumnValue,column.DataOperator.ToString(), parameter);
expression = expression == null ? exp : Expression.AndAlso(expression, exp);
results = DataContext.MessageType.Where(Expression.Lambda<Func<Media, bool>>(expression, parameter));
}
public static BinaryExpression MakeQueryFilter(Type type, string propertyName, object value, string dataoperator, ParameterExpression parameter)
{
//var type = oType.GetType();
object queryvalue = null;
var property = type.GetProperty(propertyName);
Type propertyType = property.PropertyType;
if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
propertyType = propertyType.GetGenericArguments()[0];
// convert the value appropriately
if (propertyType == typeof(System.Int32))
queryvalue = Convert.ToInt32(value);
if (property.PropertyType == typeof(DateTime))
queryvalue = Convert.ToDateTime(value);
if (property.PropertyType == typeof(Double))
queryvalue = Convert.ToDouble(value);
if (property.PropertyType == typeof(String))
queryvalue = Convert.ToString(value);
if (property.PropertyType == typeof(Guid))
queryvalue = new Guid(value.ToString());
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var constantValue = Expression.Constant(queryvalue);
Type[] types = new Type[2];
types.SetValue(typeof(Expression), 0);
types.SetValue(typeof(Expression), 1);
var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });
return equality2;
}
The problem I am encountering is when I want to query through a relationship to a value in another table, and even go two->nth relationships deep. Something like this:
Name, Joe, Equals
Age, 35, Less Than
Jobs.Occupation, Geek, Equals
Jobs.Occupation.Salary.Rate, 1000, Greater Than
I have no problem writing the LINQ query by hand:
var results = from m in DataContext.MessageType
where m.Name == "Joe"
& m.Age == 35
& m.Jobs.Occupation == "Geek"
& m.Jobs.Occupation.Salaray.Rate >= 1000
select m;
Any pointers how I can dynamically create this query? Any help is greatly appreciated. Thanks.
Eric S.
Note the use of expanding PropertyAccess.
{
BinaryExpression expression = null;
ParameterExpression parameter = Expression.Parameter(typeof(DataContext.Table), "p");
string columns = "Relation1.Relation2.Column1";
string value = "ABC123";
BinaryExpression exp = MakeQueryFilter(typeof(DataContext.Table), columns,value,'Equal', parameter);
expression = expression == null ? exp : Expression.AndAlso(expression, exp);
results = DataContext.Table.Where(Expression.Lambda<Func<Table, bool>>(expression, parameter));
}
public static BinaryExpression MakeQueryFilter(Type type, string propertyNames, object value, string dataoperator, ParameterExpression parameter)
{
string[] acolumns = columns.Split('.');
var property = typeof (MediaEvent).GetProperty(acolumns[0]);
MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);
Type propertyType = property.PropertyType;
if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
propertyType = propertyType.GetGenericArguments()[0];
if (acolumns.Length > 1)
{
for (int i = 1; i < acolumns.Length; i++)
{
propertyAccess = Expression.MakeMemberAccess(propertyAccess, propertyAccess.Type.GetProperty(acolumns[i]));
}
}
object queryvalue = null;
// convert the value appropriately
if (propertyType == typeof(System.Int32))
queryvalue = Convert.ToInt32(value);
if (property.PropertyType == typeof(DateTime))
queryvalue = Convert.ToDateTime(value);
if (property.PropertyType == typeof(Double))
queryvalue = Convert.ToDouble(value);
if (property.PropertyType == typeof(String))
queryvalue = Convert.ToString(value);
if (property.PropertyType == typeof(Guid))
queryvalue = new Guid(value.ToString());
var constantValue = Expression.Constant(queryvalue);
Type[] types = new Type[2];
types.SetValue(typeof(Expression), 0);
types.SetValue(typeof(Expression), 1);
var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });
return equality2;
}
I had the same issue. Thanks for the answer! Very useful!
You wrote this:
caller
{
BinaryExpression expression = null;
ParameterExpression parameter = Expression.Parameter(typeof(DataContext.Table), "p");
string columns = "Relation1.Relation2.Column1";
string value = "ABC123";
BinaryExpression exp
= MakeQueryFilter(typeof(DataContext.Table),
columns,value,'Equal', parameter);
expression = expression == null ? exp : Expression.AndAlso(expression, exp);
results = DataContext.Table.Where(
Expression.Lambda<Func<Table, bool>>(expression, parameter));
}
public static BinaryExpression MakeQueryFilter(Type type, string propertyNames, object value, string dataoperator, ParameterExpression parameter)
{
string[] acolumns = columns.Split('.');
var property = typeof (MediaEvent).GetProperty(acolumns[0]);
MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);
Type propertyType = property.PropertyType;
if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
propertyType = propertyType.GetGenericArguments()[0];
if (acolumns.Length > 1)
{
for (int i = 1; i < acolumns.Length; i++)
{
propertyAccess = Expression.MakeMemberAccess(propertyAccess, propertyAccess.Type.GetProperty(acolumns[i]));
}
}
object queryvalue = null;
// convert the value appropriately
if (propertyType == typeof(System.Int32))
queryvalue = Convert.ToInt32(value);
if (property.PropertyType == typeof(DateTime))
queryvalue = Convert.ToDateTime(value);
if (property.PropertyType == typeof(Double))
queryvalue = Convert.ToDouble(value);
if (property.PropertyType == typeof(String))
queryvalue = Convert.ToString(value);
if (property.PropertyType == typeof(Guid))
queryvalue = new Guid(value.ToString());
var constantValue = Expression.Constant(queryvalue);
Type[] types = new Type[2];
types.SetValue(typeof(Expression), 0);
types.SetValue(typeof(Expression), 1);
var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });
return equality2;
}
Maybe better if you write this:
public static BinaryExpression MakeQueryFilter<T>(string propertyNames, object value, string dataoperator, ParameterExpression parameter) where T : class
{
string[] props = propertyNames.Split('.');
var property = typeof(T).GetProperty(props[0]);
MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);
if (props.Length > 1)
{
for (int i = 1; i < props.Length; i++)
{
property = propertyAccess.Type.GetProperty(props[i]);
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
}
}
Type propertyType = property.PropertyType;
if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable)))
propertyType = propertyType.GetGenericArguments()[0];
object queryvalue = null;
// convert the value appropriately
if (propertyType == typeof(System.Int32))
queryvalue = Convert.ToInt32(value);
if (property.PropertyType == typeof(DateTime))
queryvalue = Convert.ToDateTime(value);
if (property.PropertyType == typeof(Double))
queryvalue = Convert.ToDouble(value);
if (property.PropertyType == typeof(String))
queryvalue = Convert.ToString(value);
if (property.PropertyType == typeof(Guid))
queryvalue = new Guid(value.ToString());
var constantValue = Expression.Constant(queryvalue);
Type[] types = new Type[2];
types.SetValue(typeof(Expression), 0);
types.SetValue(typeof(Expression), 1);
var methodInfo = typeof(Expression).GetMethod(dataoperator, types);
var equality2 = (BinaryExpression)methodInfo.Invoke(null, new object[] { propertyAccess, constantValue });
return equality2;
}