I'm using Generic repository /UoW patter in my application c#
I was using EF6 ,then i moved to EF core .
My app worked well excpet for some reason my includes doesn't work , and i got exception
Interface :
TEntity GetFirstOrDefault(
Expression<Func<TEntity, bool>> filter = null,
params Expression<Func<TEntity, object>>[] includes);
Implementation (EF core):
public virtual TEntity GetFirstOrDefault(Expression<Func<TEntity, bool>> filter = null,
params Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = dbSet;
query = includes.Aggregate(query, (current, item) => EvaluateInclude(current, item));
return query.FirstOrDefault(filter);
}
In Entity Framework EF6 , it was :
foreach (Expression<Func<TEntity, object>> include in includes)
query = query.Include(include);
The EvaluateInclude function is :
private IQueryable<TEntity> EvaluateInclude(IQueryable<TEntity> current, Expression<Func<TEntity, object>> item)
{
if (item.Body is MethodCallExpression)
{
var arguments = ((MethodCallExpression)item.Body).Arguments;
if (arguments.Count > 1)
{
var navigationPath = string.Empty;
for (var i = 0; i < arguments.Count; i++)
{
var arg = arguments[i];
var path = arg.ToString().Substring(arg.ToString().IndexOf('.') + 1);
navigationPath += (i > 0 ? "." : string.Empty) + path;
}
return current.Include(navigationPath);
}
}
return current.Include(item);
}
When I call GetFirstOrDefault function like this way , it works :
internal Domain.Entities.Project GetProject(int projectId)
{
Expression<Func<Domain.Entities.Project, bool>> funcWhere = j => (!j.IsDisabled && j.ProjectId == projectId);
return UnitOfWork.Repository<Domain.Entities.Project>().GetFirstOrDefault(funcWhere,
p => p.StatusProject,
p => p.ProjectRoles.Select(t => t.Employee),
//p => p.ProjectTeams.Select(t => t.Team.TeamEmployees.Select(e => e.Employee)),
);
}
But when I un-comment the extra include , it fails :
internal Domain.Entities.Project GetProject(int projectId)
{
Expression<Func<Domain.Entities.Project, bool>> funcWhere = j => (!j.IsDisabled && j.ProjectId == projectId);
return UnitOfWork.Repository<Domain.Entities.Project>().GetFirstOrDefault(funcWhere,
p => p.StatusProject,
p => p.ProjectRoles.Select(t => t.Employee),
p => p.ProjectTeams.Select(t => t.Team.TeamEmployees.Select(e => e.Employee)),
);
}
System.InvalidOperationExceptionInvalid include path:
'Project.ProjectTeams.Team.TeamEmployees.Select(e => e.Employee)' -
couldn't find navigation for: 'Select(e => e'
Solved :
following this answer link
I added this code that parse my lambda expression of includes :
// This method is a slight modification of EF6 source code
private bool TryParsePath(Expression expression, out string path)
{
path = null;
var withoutConvert = RemoveConvert(expression);
var memberExpression = withoutConvert as MemberExpression;
var callExpression = withoutConvert as MethodCallExpression;
if (memberExpression != null)
{
var thisPart = memberExpression.Member.Name;
string parentPart;
if (!TryParsePath(memberExpression.Expression, out parentPart))
{
return false;
}
path = parentPart == null ? thisPart : (parentPart + "." + thisPart);
}
else if (callExpression != null)
{
if (callExpression.Method.Name == "Select"
&& callExpression.Arguments.Count == 2)
{
string parentPart;
if (!TryParsePath(callExpression.Arguments[0], out parentPart))
{
return false;
}
if (parentPart != null)
{
var subExpression = callExpression.Arguments[1] as LambdaExpression;
if (subExpression != null)
{
string thisPart;
if (!TryParsePath(subExpression.Body, out thisPart))
{
return false;
}
if (thisPart != null)
{
path = parentPart + "." + thisPart;
return true;
}
}
}
}
else if (callExpression.Method.Name == "Where")
{
throw new NotSupportedException("Filtering an Include expression is not supported");
}
else if (callExpression.Method.Name == "OrderBy" || callExpression.Method.Name == "OrderByDescending")
{
throw new NotSupportedException("Ordering an Include expression is not supported");
}
return false;
}
return true;
}
// Removes boxing
private Expression RemoveConvert(Expression expression)
{
while (expression.NodeType == ExpressionType.Convert
|| expression.NodeType == ExpressionType.ConvertChecked)
{
expression = ((UnaryExpression)expression).Operand;
}
return expression;
}
#endregion
Then change my EvaluateInclude function to :
private IQueryable<TEntity> EvaluateInclude(IQueryable<TEntity> current, Expression<Func<TEntity, object>> item)
{
if (item.Body is MethodCallExpression)
{
string path;
TryParsePath(item.Body, out path);
return current.Include(path);
}
return current.Include(item);
}
And it works
Related
i'm trying to refactor some code that is going to be similar in several areas. This piece of code is from a validator using fluentvalidation. I'm trying to transform the HaveUniqueNumber routine to be generic and pass in lambdas that will be used in the db query.
public class AddRequestValidator : AbstractValidator<AddRequest>
{
public AddRequestValidator()
{
RuleFor(x => x)
.Must(x => HaveUniqueNumber(x.myNumber, x.parentId))
.WithMessage("myNumber '{0}' already exists for parentId '{1}'.", x => x.myNumber, x=>x.parentId);
}
private bool HaveUniqueNumber(string number, int parentId)
{
myModel existingNumber = null;
using (var context = new myContextDb())
{
existingNumber = context.myModel.SingleOrDefault(a => a.MyNumber == number && a.ParentId == parentId);
}
return existingNumber == null;
}
}
i tried implementing something like :
public class AddRequestValidator : AbstractValidator<AddRequest>
{
public AddRequestValidator()
{
RuleFor(x => x)
.Must(x => HaveUniqueNumber<myModel>(an => an.myNumber == x.MyNumber, an => an.parentId == x.parentId))
.WithMessage("myNumber '{0}' already exists for parentId '{1}'.", x => x.myNumber, x=>x.parentId);
}
private bool HaveUniqueNumber<T>(Expression<Func<T, bool>> numberExpression, Expression<Func<T, bool>> parentExpression)
{
T existingNumber = default(T);
using (var context = new myContextDb())
{
existingNumber = context.T.SingleOrDefault(numberExpression && parentExpression);
}
return existingNumber == null;
}
}
how can i get an something similar to the original to work.
Needed to fix the following:
context.T to context.DbSet<T>()
added the ExpressionVisitor Class as referenced by the link posted by Alexei Levenkov. 457316
Added where T: class to HaveUniqueMethod
ended up with this refactored class:
private bool HaveUniqueNumber<T>(Expression<Func<T, bool>> numberExpression, Expression<Func<T, bool>> parentExpression) where T : class
{
T existingNumber = default(T);
using (var context = new myContextDb())
{
existingNumber = context.Set<T>().SingleOrDefault(numberExpression.AndAlso(parentExpression));
}
return existingNumber == null;
}
and added this extension method:
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
Based on API, I can have multiple parameters which can be used in order by. There is a function which creates a dynamic order by parameter as a string. I want to use this in .OrderBy but not sure how to do this.
API Call:
{{url}}?keyword=singer&page=12&size=5&sortby=LastName&sortby=FirstName
Code:
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(sortParameters.ToOrderBy()) // "LastName ASC, FirstName ASC"
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
Extension Method to get order by:
static class RepositoryExtensions
{
public static string ToOrderBy(this IEnumerable<SortParameter> parameters)
{
return string.Join(", ", parameters.Select(p => p.SortBy + (p.Descending ? " DESC" : " ASC")));
}
}
Output:
"LastName ASC, FirstName ASC"
Extension method to accept dynamic LINQ:
public static class Extension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof(T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi); // Errors out here.
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
}
Error:
System.ArgumentNullException: Value cannot be null.
Parameter name: property
Scrren shot:
This is the first time working with this complex query so not sure how to do this. I can add more info if needed.
It looks like the error occurs because pi is null. And it is null because, I would assume, the class standing in for the T generic doesn't have a property named LastName ASC, FirstName ASC. I would try something like the following:
var props = property.Split(",");
... //this code stays the same
foreach(string prop in props) {
var propNameAndDirection = prop.Split(" ");
PropertyInfo pi = type.GetProperty(propNameAndDirection[0]);
... //continue as necessary, using propNameAndDirection[1]
... //to decide OrderBy or OrderByDesc call
Hopefully this sets you in the right direction.
After some trial and error, I am able to find an answer.
Tested with followings:
.OrderBy("LastName ASC, FirstName ASC")
.OrderBy("LastName ASC")
.OrderBy("LastName ASC,FirstName DESC")
Linq:
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(o => o.Equals(sortParameters.ToOrderBy()))
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
Helper Class:
public static class OrderByHelper
{
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string orderBy)
{
return enumerable.AsQueryable().OrderBy(orderBy).AsEnumerable();
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, string orderBy)
{
foreach (var orderByInfo in ParseOrderBy(orderBy))
{
collection = ApplyOrderBy(collection, orderByInfo);
}
return collection;
}
private static IQueryable<T> ApplyOrderBy<T>(IQueryable<T> collection, OrderByInfo orderByInfo)
{
var props = orderByInfo.PropertyName.Split('.');
var type = typeof (T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
string methodName;
if (!orderByInfo.Initial && collection is IOrderedQueryable<T>)
{
methodName = orderByInfo.Direction == SortDirection.Ascending ? "ThenBy" : "ThenByDescending";
}
else
{
methodName = orderByInfo.Direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
}
return (IOrderedQueryable<T>) typeof (Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof (T), type)
.Invoke(null, new object[] {collection, lambda});
}
private static IEnumerable<OrderByInfo> ParseOrderBy(string orderBy)
{
if (string.IsNullOrEmpty(orderBy))
{
yield break;
}
var items = orderBy.Split(',');
var initial = true;
foreach (var item in items)
{
var pair = item.Trim().Split(' ');
if (pair.Length > 2)
{
throw new ArgumentException(
$"Invalid OrderBy string '{item}'. Order By Format: Property, Property2 ASC, Property2 DESC");
}
var prop = pair[0].Trim();
if (string.IsNullOrEmpty(prop))
{
throw new ArgumentException(
"Invalid Property. Order By Format: Property, Property2 ASC, Property2 DESC");
}
var dir = SortDirection.Ascending;
if (pair.Length == 2)
{
dir = "desc".Equals(pair[1].Trim(), StringComparison.OrdinalIgnoreCase)
? SortDirection.Descending
: SortDirection.Ascending;
}
yield return new OrderByInfo {PropertyName = prop, Direction = dir, Initial = initial};
initial = false;
}
}
private class OrderByInfo
{
public string PropertyName { get; set; }
public SortDirection Direction { get; set; }
public bool Initial { get; set; }
}
private enum SortDirection
{
Ascending = 0,
Descending = 1
}
Referances:
Dynamic LINQ OrderBy on IEnumerable<T>
http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html
If I understood the problem correctly.
Expression<Func<TEntity, TKey>> genericParameter = null;
genericParameter = x => x.foo;
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(genericParameter)
i have a problem to use the dynamic parameters inside orderby linq expression
SearchExp function
public Expression<Func<EmailAflAwmMessageDM, bool>> SearchXpr(string param, string q)
{
if (param == "to")
return e => e.to_msg.Contains(q);
else if (param == "from")
return e => e.from_msg.Contains(q);
else if (param == "cc")
return e => e.cc_msg.Contains(q);
else if (param == "bcc")
return e => e.bcc_msg.Contains(q);
else if (param == "subject")
return e => e.subject.Contains(q);
else
return e => e.body_text.Contains(q);
}
filterExp function
public Expression<Func<EmailAflAwmMessageDM, bool>> FiltertXpr(string filter, string value)
{
if (filter == "attachments")
return e => e.attachments == value;
else if (filter == "flagged")
return e => e.flagged == value;
else
return e => e.seen == value;
}
IQueryable function
private IQueryable SearchFilter(string param,string q,string filter,
string value,string sort,string dir)
{
var searchXpr = SearchXpr(param, q);
var filterXpr = FiltertXpr(filter, value);
var emailmessage =
db.EmailAflAwmMessage.
Where(filterXpr).Where(searchXpr)
.OrderByDescending(a => a.msg_date).Select(a =>
new
{
a.subject,
a.msg_date,
});
return emailmessage;
}
The above code is working, but i need OrderBy in dynamic way.
as i have 2 parameters sort( mean its the parameter name ) and dir (mean ascending or descending) like i want orderby(parameter name) dir
please help me, i appreciate your valuable time and suggestion, and also suggest me any alternate with simple way. thanks.
I suggest you to read about Expression's tree's, the code bellow is for didatic , but I think that will help you:
public static class ExpressionBuilder
{
private static readonly MethodInfo ToStringMethod = typeof(object).GetMethod("ToString");
private static readonly MethodInfo StringContainsMethod = typeof(string).GetMethod("Contains");
public static Func<T, object> Selector<T>(string prop)
{
var type = typeof(T);
var param = Expression.Parameter(type);
return Expression.Lambda<Func<T, object>>(Expression.Property(param, type.GetProperty(prop)), param).Compile();
}
public static Expression<Func<T, bool>> BuildFilterPredicate<T>(string q)
{
var query = Expression.Constant(q);
var type = typeof(T);
var lbdSelector = Expression.Parameter(type);
var predicates = type.GetProperties().SelectMany(p => PredicateContainsBuilder(lbdSelector, p, query)).ToList();
Expression body = predicates[0];
body = predicates.Skip(1).Aggregate(body, Expression.OrElse);
return Expression.Lambda<Func<T, bool>>(body, lbdSelector);
}
private static IEnumerable<MethodCallExpression> PredicateContainsBuilder(Expression lbdSelector, PropertyInfo prop, Expression query)
{
if (prop.PropertyType.IsClass)
return new List<MethodCallExpression> { Expression.Call(Expression.Call(Expression.Property(lbdSelector, prop), ToStringMethod), StringContainsMethod, query) };
var properties = prop.PropertyType.GetProperties();
return properties.Select(p => Expression.Call(Expression.Call(Expression.Property(lbdSelector, p), ToStringMethod), StringContainsMethod, query)).ToList();
}
}
So now you do this in your method:
Note:
I supose that the entity is EmailMessage so i use that to generate the predicate;
It doesn't search in depth;
it will search in all properties and doesn't use the string param to define what property to match;
private IQueryable SearchFilter(string param,string q,string filter,string value,string sort,string dir)
{
var emailMessage = db.EmailAflAwmMessage
.Where(ExpressionBuilder.BuildFilterPredicate<EmailMessage>(q))
.OrderBy(ExpressionBuilder.Selector<EmailMessage>(sort))
.Select(m=> new{m.subject,m.msg_date});
return emailmessage;
}
i have got the easy solution and now its working with me, there are more alternatives but i just to share my answer:
SortXpr function
private IQueryable SortXpr(IQueryable<EmailAflAwmMessageDM> email ,string sort,string dir) {
if (sort.Contains("to"))
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.to_msg);
}
else
{
return email.OrderByDescending(e => e.to_msg);
}
}
else if (sort.Contains("from"))
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.from_msg);
}
else
{
return email.OrderByDescending(e => e.from_msg);
}
}
else if (sort.Contains("subject"))
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.subject);
}
else
{
return email.OrderByDescending(e => e.subject);
}
}
else
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.msg_date);
}
else
{
return email.OrderByDescending(e => e.msg_date);
}
}
}
FilterXpr function
private Expression<Func<EmailAflAwmMessageDM, bool>> FiltertXpr(string filter, string value)
{
if (filter == "attachments")
return e => e.attachments == value;
else if (filter == "flagged")
return e => e.flagged == value;
else
return e => e.seen == value;
}
SearchXpr function
private Expression<Func<EmailAflAwmMessageDM, bool>> SearchXpr(string param, string q)
{
if (param == "to")
return e => e.to_msg.Contains(q);
else if (param == "from")
return e => e.from_msg.Contains(q);
else if (param == "cc")
return e => e.cc_msg.Contains(q);
else if (param == "bcc")
return e => e.bcc_msg.Contains(q);
else if (param == "subject")
return e => e.subject.Contains(q);
else
return e => e.body_text.Contains(q);
}
SearchFilterCondition function
private IQueryable SearchFilterCondition(string param,string q
,string filter,string value,string sort,string dir)
{
var searchXpr = SearchXpr(param, q);
var filterXpr = FiltertXpr(filter, value);
IQueryable<EmailAflAwmMessageDM>
EmailAflAwmMessagejc = db.EmailAflAwmMessage.Where(filterXpr).Where(searchXpr);
return SortXpr(EmailAflAwmMessagejc, sort, dir);
}
thanks for the stackoverflow community, i appreciate your valuable time, thanks again.
I would like to perform LINQ where clause inside a method.
For example:
using (BP_TTOKEntities db = new BP_TTOKEntities(_dto.IdTenant))
{
var res = db.doc003fornitura
if (fornitura.Numero != null) //Filtro numero
{
if (!fornitura.Numero.LBoundIsNull) res = res.Where(x => x.fornitura_nro >= fornitura.Numero.LBound);
if (!fornitura.Numero.UBoundIsNull) res = res.Where(x => x.fornitura_nro <= fornitura.Numero.UBound);
}
}
I would replace:
if (!fornitura.Numero.LBoundIsNull) res = res.Where(x => x.fornitura_nro >= fornitura.Numero.LBound);
if (!fornitura.Numero.UBoundIsNull) res = res.Where(x => x.fornitura_nro <= fornitura.Numero.UBound);
with something like this:
res = fornitura.Numero.Where<doc003fornitura>(x.fornitura_nro);
Is it possible? How can I make the method?
Thanks Luigi.
I solved in this way.
This is the call:
if (fornitura.Numero != null) res = fornitura.Numero.Compare(res, x => x.fornitura_nro);
These are the methods of the class:
public IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Expression<Func<TEntity, Nullable<short>>> func)
{
return _Compare(source, func);
}
public IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Expression<Func<TEntity, Nullable<int>>> func)
{
return _Compare(source, func);
}
public IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Expression<Func<TEntity, Nullable<DateTime>>> func)
{
return _Compare(source, func);
}
public IQueryable<TEntity> Compare<TEntity>(IQueryable<TEntity> source, Expression<Func<TEntity, string>> func)
{
return _Compare(source, func);
}
private IQueryable<TEntity> _Compare<TEntity>(IQueryable<TEntity> source, object func)
{
IQueryable<TEntity> res = source;
Type type = func.GetType();
Expression funcBody = (Expression)type.GetProperty("Body").GetValue(func);
IEnumerable<ParameterExpression> funcParameters = (IEnumerable<ParameterExpression>)type.GetProperty("Parameters").GetValue(func);
if (!this.LBoundIsNull)
{
Expression ge = _Comparison(funcBody, Expression.Constant(_lBound), ExpressionType.GreaterThanOrEqual);
var lambda = Expression.Lambda<Func<TEntity, bool>>(ge, funcParameters);
res = res.Where(lambda);
}
if (!this.UBoundIsNull)
{
Expression le = _Comparison(funcBody, Expression.Constant(_uBound), ExpressionType.LessThanOrEqual);
var lambda = Expression.Lambda<Func<TEntity, bool>>(le, funcParameters);
res = res.Where(lambda);
}
return res;
}
private Expression _Comparison(Expression left, Expression right, ExpressionType expressionType)
{
if (left.Type.IsNullable() && !right.Type.IsNullable())
right = Expression.Convert(right, left.Type);
else if (!left.Type.IsNullable() && right.Type.IsNullable())
left = Expression.Convert(left, right.Type);
if (left.Type == typeof(string))
{
var method = left.Type.GetMethod("CompareTo", new[] { typeof(string) });
var result = Expression.Call(left, method, right);
return Expression.MakeBinary(expressionType, result, Expression.Constant(0));
}
else
switch (expressionType)
{
case ExpressionType.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(left, right);
case ExpressionType.LessThanOrEqual:
return Expression.LessThanOrEqual(left, right);
default:
return Expression.Equal(left, right);
}
}
Thank you!
I have an entity class like
public class BookPage {
public int PageIndex { get; set; }
}
then I have an expression:
Expression<Func<int, bool>> pageIndexCondition = idx => idx == 1;
and the expression I want:
Expression<Func<BookPage, bool>> pageCondition = bookPage => bookPage.PageIndex == 1;
The question: How do I use pageIndexCondition to do LINQ-to-SQL query, or how can I convert pageIndexCondition into pageCondition?
Edit: Another solution that would be less elegant, but still meet my requirement is:
Expression<Func<T, bool>> GetPageIndexCondition(Expression<Func<T, int>> selector) {
return (T item) => selector(item) < 10; // This won't work because selector is Expression, so how to implement this correctly?
}
...
var pageCondition = GetPageIndexCondition(page => page.PageIndex);
I like doing these things, though as others have said, there's probably more efficient and better ways to do it:
void Main()
{
Expression<Func<int, bool>> pageIndexCondition = idx => idx == 1;
Expression<Func<BookPage, bool>> converted = ExpressionConverter.Convert(pageIndexCondition);
}
public class ExpressionConverter : ExpressionVisitor
{
public static Expression<Func<BookPage, bool>> Convert(Expression<Func<int, bool>> e)
{
var oldParameter = e.Parameters.First();
var newParameter = Expression.Parameter(typeof(BookPage), "bp");
Expression<Func<BookPage, int>> x = (BookPage bp) => bp.PageIndex;
var property = ((x.Body as MemberExpression).Member as PropertyInfo);
var memberAccess = Expression.Property(newParameter, property);
var converter = new ExpressionConverter(oldParameter, memberAccess);
return (Expression<Func<BookPage, bool>>)Expression.Lambda(converter.Visit(e.Body), newParameter);
}
private ParameterExpression pe;
private Expression replacement;
public ExpressionConverter(ParameterExpression pe, Expression replacement)
{
this.pe = pe;
this.replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if(node == pe)
return replacement;
return base.VisitParameter(node);
}
}
var pages = new List<BookPage>
{
new BookPage { PageIndex = 1 },
new BookPage { PageIndex = 2 }
};
Expression<Func<BookPage, bool>> pageCondition = bookPage => bookPage.PageIndex == 1;
BookPage result = pages.AsQueryable().Single(pageCondition);
If you want a generic select by id you will have to do something like;
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null)
{
if (filter != null)
{
query = query.Where(filter);
}
}
This goes in your generic repository.