I have something like this:
if (sort == "Customer")
{
if (sortDirection == SortDirection.Descending)
myList = myList.OrderByDescending(e => e.SiteOrganization.Organization.Name).ToList();
else
myList = myList.OrderBy(e => e.SiteOrganization.Organization.Name).ToList();
}
if (sort == "RequestType")
{
if (sortDirection == SortDirection.Descending)
myList = myList.OrderByDescending(e => e.TypeId).ToList();
else
myList = myList.OrderBy(e => e.TypeId).ToList();
}
if (sort == "RequestedByShort")
{
if (sortDirection == SortDirection.Descending)
myList = myList.OrderByDescending(e => e.RequestedByUser.ShortName).ToList();
else
myList = myList.OrderBy(e => e.RequestedByUser.ShortName).ToList();
}
I would like to clean this up to have something like
if (sortDirection == SortDirection.Descending)
myList = myList.OrderByDescending(e => e.RequestedByUser.ShortName).ToList();
else
myList = myList.OrderBy(e => e.RequestedByUser.ShortName).ToList();
So that I only have ONE LINQ query no matter what "sort" it is. Any suggestions?
If you move your sort logic into a method, you can pass the predicate straight in e.g.
public IList<TSource> SortBy<TSource, TMember>(IEnumerable<TSource> list, Func<TSource, TMember> selector, SortDirection direction)
{
if (direction == SortDirection.Descending)
return list.OrderByDescending(selector).ToList();
else
return list.OrderBy(selector).ToList();
}
...
if (sort == "Customer") {
list = SortBy(list, x => x.SiteOrganization.Organization.Name, SortDirection.Descending);
} else if (sort == "RequestType") {
list = SortBy(list, x => x.TypeId, SortDirection.Ascending);
} else if (sort == "RequestedByShort") {
list = SortBy(list, x => x.RequestedByUser.ShortName, SortDirection.Descending);
}
Live demo
If you wanted to use this as a general solution to all lists, you could create it as an extension method
public static class ListExt
{
public static IList<TSource> SortBy<TSource, TMember>(this IEnumerable<TSource> list, Func<TSource, TMember> selector, SortDirection direction)
{
if (direction == SortDirection.Descending) {
return list.OrderByDescending(selector).ToList();
} else {
return list.OrderBy(selector).ToList();
}
}
}
...
list = list.SortBy(x => x.TypeId, SortDirection.Ascending);
Related
Is there anyway I can optimize this code into shorter?
MakeList, TrimList and etc are List type.
and Vehicle are models.
My problem is code is very long. I have 20 property in model.
if (MakeList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => MakeList.Contains(b.Vehicle.Make));
}
if (TrimList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => TrimList.Contains(b.Vehicle.Trim));
}
if (ModelList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => ModelList.Contains(b.Vehicle.Model));
}
if (StockNoList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => StockNoList.Contains(b.Vehicle.StockNo));
}
if (BodyStyleList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => BodyStyleList.Contains(b.Vehicle.Body));
}
if (ExtColorList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => ExtColorList.Contains(b.Vehicle.Exterior));
}
return bidVehicles;
For LINQ to Objects:
public static class FilterExt
{
public static IEnumerable<TItem> ApplyFilter<TProp, TItem>(this IEnumerable<TItem> list, List<TProp> filter, Func<TItem, TProp> prop)
{
if (filter == null || filter.Count == 0)
{
return list;
}
return list.Where(x => filter.Contains(prop.Invoke(x)));
}
}
...
var filtered = bidVehicles
.ApplyFilter(MakeList, x => x.Vehicle.Make)
.ApplyFilter(TrimList, x => x.Vehicle.Trim).ToList();
If you use EF (means bidVehicles is IQueryable) you have to write expression for each property because you need full predicate Expression<Func<BidVehicle, bool>> not just Func<BidVehicle, TProp>.
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 have method like this :
GetUsallyOpeningClosingHour(Func<OpeningDay, TimeSpan> groupByRule)
{
var openingClosingHours = listOfSpecificDayOfWeek.GroupBy(groupByRule).OrderByDescending(x => x.Key);
}
and the problem is that I can't stick all the time with OrderByDescending depends on groupByRule parameter sometimes it has to be orderByDescending or OrderBy
I don't want to depend on this parameter, so I could pass another one for that,
Right now I call my method this way:
GetUsallyOpeningClosingHour(x => x.From)
or
GetUsallyOpeningClosingHour(x => x.To)
How can I pass orderBy type as well ?
The simplest way is adding a parameter, which will specify an order in your collection.
public void GetUsallyOpeningClosingHour(
Func<OpeningDay, TimeSpan> groupByRule,
bool orderByDesc = false)
{
var groupedDays = listOfSpecificDayOfWeek.GroupBy(groupByRule);
var openingClosingHours =
orderByDesc
? groupedDays.OrderByDescending(x => x.Key)
: groupedDays.OrderBy(x => x.Key);
}
It could be a boolean or custom enum (I prefer enum, because it actually specifies a kind of ordering operation, while boolean specifies whether collection should be ordered by desc or not).
public enum OrderingType
{
Ascending,
Descending,
None
}
Or you could provide an additional Func, which will perform an ordering operation. But its signature will be awkward.
public static void GetUsallyOpeningClosingHour(
Func<OpeningDay, TimeSpan> groupByRule,
Func<IEnumerable<IGrouping<TimeSpan, OpeningDay>>,
IEnumerable<IGrouping<TimeSpan, OpeningDay>>> orderBy)
{
var groupedDays = listOfSpecificDayOfWeek.GroupBy(groupByRule);
var openingClosingHours = orderBy(groupedDays);
}
I guess you could create your own OrderBy extension that let you select ascending/descending based on a parameter.
Something like this:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
bool descending
)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
You can also use an enum instead of the boolean to make things more readable when calling this method.
This is the most direct way to parameterise for OrderBy and OrderByDescending. Fortunately the type can be inferred for you by Visual Studio; unfortunately the type is long to write out. I added the static void and the initializer for listOfSpecificDayOfWeek so that this is easy to paste into a program for testing.
static void GetUsallyOpeningClosingHour(
Func<OpeningDay, TimeSpan> groupByRule,
Func<IEnumerable<IGrouping<TimeSpan, OpeningDay>>,
Func<IGrouping<TimeSpan, OpeningDay>, TimeSpan>,
IOrderedEnumerable<IGrouping<TimeSpan, OpeningDay>>> order)
{
IEnumerable<OpeningDay> listOfSpecificDayOfWeek = null;
var openingClosingHours = order(listOfSpecificDayOfWeek.GroupBy(groupByRule), x => x.Key);
}
You can call this function like this:
GetUsallyOpeningClosingHour(x => x.From, Enumerable.OrderByDescending);
GetUsallyOpeningClosingHour(x => x.From, Enumerable.OrderBy);
As other answers indicate, you can also just use a boolean flag to indicate ascending or descending order.
You would have to pass in a parameter as there's no way for the method to know which direction you want to sort in based only on the parameter (eg. From/To).
public [return-type] GetUsallyOpeningClosingHour(Func<OpeningDay, TimeSpan> groupByRule, bool isAscending)
{
var openingClosingHours = listOfSpecificDayOfWeek.GroupBy(groupByRule);
if (isAscending)
{
openingClosingHours = openingClosingHours.OrderBy(x => x.Key);
}
else
{
openingClosingHours = openingClosingHours.OrderByDescending(x => x.Key);
}
// Return openingClosingHours? It's not clear how you're using this variable.
}
public List<Book> Books(string orderField, bool desc, int skip, int take)
{
var propertyInfo = typeof(Book).GetProperty(orderField);
return _context.Books
.Where(...)
.OrderBy(p => !desc ? propertyInfo.GetValue(p, null) : 0)
.ThenByDescending(p => desc ? propertyInfo.GetValue(p, null) : 0)
.Skip(skip)
.Take(take)
.ToList();
}
this is my code sample:
public IQueryable<T> GetAllbySearch(
int pageNumber = 1, int pageSize = 10,
Dictionary<string, dynamic> filterParams = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null,
bool allIncluded = false
, Func<IQueryable<T>, IOrderedQueryable<T>> order = null)
{
var query = _entity.AsQueryable();
if (include != null && !allIncluded)
{
query = include(query);
}
if (allIncluded && include == null)
{
foreach (var property in _context.Model.FindEntityType(typeof(T)).GetNavigations()
.Where(r => !r.IsCollection()))
query = query.Include(property.Name);
}
if (filterParams != null && filterParams.Any())
{
if (filterParams.Any(r => r.Value != null))
{
var expression = GetSearchFilter(filterParams);
if (order != null)
{
return order(query.Where(expression));
}
else
{
return query.Where(expression));
}
}
}
if (order != null)
{
return order(query);
}
else
{
return query;
}
}
The user have the option to sort by price or by date listed. Both can be sorted in ascending or descending. They both can be used or one of them.
What is the best practical method to use in such a situation ?
Can I make 1 linq statement and replace the words "ascending"/"descending" or remove them from the statement by modifying a string ? (in other words, construct the linq statement like sql?)
Instead of relying on strings, you can use the SortOrder enum:
public MyCollection OrderedByPrice(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
{
return new MyCollection(this.OrderBy(x => x.Price));
}
else
{
return new MyCollection(this.OrderByDescending(x => x.Price));
}
}
As per your comments, if you want to order by both you could use ThenBy
public MyCollection OrderedByPriceThenByDate(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
{
return new MyCollection(this.OrderBy(x => x.Price)
.ThenBy(y => y.Date));
}
else
{
return new MyCollection(this.OrderByDescending(x => x.Price)
.ThenByDescending(y => y.Date));
}
}
you can also build an expression to do it
public IEnumerable<T> ExecuteSort<T>(
IQueryable<T> src, Expression<Func<T,bool>> predicate, SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
{
return src.OrderBy(predicate));
}
else
{
return src..OrderByDescending(predicate));
}
}
ExecuteSort(src, v => v.Price, ortOrder.Ascending);
Hello i got some method that generating where statment programmatically how can i move where generation to other class method anyone can help ?
public static List<MME.Objects.TypedLists.InvoiceList> GetList(List<MMPFramework.SearchParameter> parameter)
{
MME.Objects.Invoice Invoice = null;
MME.Objects.Contractor Contractor = null;
MME.Objects.Contract Contract = null;
MME.Objects.TypedLists.InvoiceList invoiceList= null;
var t = MME.DAL.NhSessionHelper.GetCurrentSession().QueryOver<MME.Objects.Invoice>(() => Invoice);
foreach (var searchParameter in parameter)
{
if(searchParameter.Expression == "Like")
{
t.Where(Restrictions.Like(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else if (searchParameter.Expression == "Eq")
{
t.Where(Restrictions.Eq(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else if (searchParameter.Expression == "Between")
{
t.Where(Restrictions.Between(searchParameter.PropertyName, searchParameter.ObjectValueLo,searchParameter.ObjectValueHi));
}
else if(searchParameter.Expression == "Gt")
{
t.Where(Restrictions.Gt(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else if (searchParameter.Expression == "Lt")
{
t.Where(Restrictions.Lt(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else
{
//todo more
}
//t.Where(Restrictions.Eq(searchParameter.PropertyName, searchParameter.ObjectValue));
}
t.JoinQueryOver(() => Invoice.Contractor, () => Contractor, JoinType.LeftOuterJoin)
.JoinQueryOver(() => Invoice.Contract, () => Contract, JoinType.LeftOuterJoin)
.Select(Projections.Property(() => Invoice.Id).WithAlias(() => invoiceList.Id),
Projections.Property(() => Invoice.Number).WithAlias(() => invoiceList.InvoiceNumber),
Projections.Property(() => Contractor.Name).WithAlias(() => invoiceList.ContractorName),
Projections.Property(() => Contract.Number).WithAlias(() => invoiceList.ContractNumber)
)
.TransformUsing(Transformers.AliasToBean<MME.Objects.TypedLists.InvoiceList>());
return t.List<MME.Objects.TypedLists.InvoiceList>().ToList();
}
I've tried with this but it seems to not work.... Hope someone was doing something and can help me to handle with it.
public class BaseList
{
public object WhereGenerator(object ob)
{
QueryOver Ded = ob as QueryOver;
return null;
}
}
foreach (var restriction in BaseList.Createrestrictions(parameter))
{
t.Where(restriction);
}
public class BaseList
{
public IEnumerable<AbstractCriterion> Createrestrictions(List<MMPFramework.SearchParameter> parameter)
{
return parameter.Select(ToCritieria);
}
private AbstractCriterion ToCritieria(SearchParameter searchParameter)
{
if(searchParameter.Expression == "Like")
{
return Restrictions.Like(searchParameter.PropertyName, searchParameter.ObjectValueLo);
}
else ...
}
}