I have a function who generate the lambda expression like this
d => d.Defendeurs.Any(DossierTiers => DossierTiers.Tiers.TiersLiesEnfantsActifs.Any(TiersLie => TiersLie.TiersEnfant.AdressePrincipale.Adresse.CodePostal.Contains("45")))
All work but sometimes I got NullExceptionReference on some class.
So I need to check if some property is null.
I think, I must add some code in the BuildLambda function, but all my test doesn't work.
Some help are welcome
public static Expression GetExpression(Expression parameter, object Operator, object value, params string[] properties)
{
Expression resultExpression = null;
Expression childParameter, navigationPropertyPredicate;
Type childType = null;
if (properties.Count() > 1)
{
parameter = Expression.Property(parameter, properties[0]);
var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
if (isCollection)
{
childType = parameter.Type.GetGenericArguments()[0];
childParameter = Expression.Parameter(childType, childType.Name);
}
else
{
childParameter = parameter;
}
var innerProperties = properties.Skip(1).ToArray();
navigationPropertyPredicate = GetExpression(childParameter, Operator, value, innerProperties);
if (isCollection)
{
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate);
resultExpression = BuildLambda(parameter, navigationPropertyPredicate);
}
else
{
resultExpression = navigationPropertyPredicate;
}
}
else
{
ConstantExpression right = null;
var childProperty = parameter.Type.GetProperty(properties[0]);
var left = Expression.Property(parameter, childProperty);
right = (value != null) ? right = Expression.Constant(value, value.GetType()) : Expression.Constant(string.Empty);
navigationPropertyPredicate = GetExpression(left, right, Operator);
resultExpression = BuildLambda(parameter, navigationPropertyPredicate);
}
return resultExpression;
}
private static Expression BuildLambda(Expression parameter, Expression predicate)
{
var resultParameterVisitor = new ParameterVisitor();
resultParameterVisitor.Visit(parameter);
var resultParameter = resultParameterVisitor.Parameter;
return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}
private static Expression GetExpression(MemberExpression left, ConstantExpression right, Object p)
{
MethodInfo c = null;
if (p is OperatorUsedWithString)
{
switch ((OperatorUsedWithString)p)
{
case OperatorUsedWithString.CommencePar:
c = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
return Expression.Call(left, c, right);
case OperatorUsedWithString.Contient:
c = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
return Expression.Call(left, c, right);
case OperatorUsedWithString.TerminePar:
c = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
return Expression.Call(left, c, right);
case OperatorUsedWithString.Egal:
c = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
return Expression.Call(left, c, right);
}
}
if (p is OperatorUsedWithNumber)
{
switch ((OperatorUsedWithNumber)p)
{
case OperatorUsedWithNumber.Egal:
return Expression.Equal(left, right);
case OperatorUsedWithNumber.Inferieur:
return Expression.LessThan(left, right);
case OperatorUsedWithNumber.InferieurEgal:
return Expression.LessThanOrEqual(left, right);
case OperatorUsedWithNumber.Superieur:
return Expression.GreaterThan(left, right);
case OperatorUsedWithNumber.SuperieurEgal:
return Expression.GreaterThanOrEqual(left, right);
}
}
if (p is OperatorUsedWithDateTime)
{
switch ((OperatorUsedWithDateTime)p)
{
case OperatorUsedWithDateTime.PasPresent:
return Expression.Equal(left, Expression.Constant(null));
case OperatorUsedWithDateTime.Present:
return Expression.NotEqual(left, Expression.Constant(null));
}
}
throw new NotImplementedException();
}
}
Related
I hope somebody can guide and help me with this. We have an inherited project that uses ExpressionHelper class. Basically, this Expression Helper will return an IQueryable that build a dynamic query base on the search term that the user provided.
For example, I have the below code where I pass 2 search terms.
IQueryable<UserEntity> modifiedQuery = _uow.UserRepository.GetAll();;
var searchTerms = new List<SearchTerm>
{
new SearchTerm { Name = "FirstName", Operator = "eq", Value = "Bob" },
new SearchTerm { Name = "FirstName", Operator = "eq", Value = "John" }
};
foreach (var searchTerm in searchTerms)
{
var propertyInfo = ExpressionHelper
.GetPropertyInfo<TEntity>(searchTerm.EntityName ?? searchTerm.Name);
var obj = ExpressionHelper.Parameter<TEntity>();
var left = ExpressionHelper.GetPropertyExpression(obj, propertyInfo);
var right = searchTerm.ExpressionProvider.GetValue(searchTerm.Value);
var comparisonExpression = searchTerm.ExpressionProvider
.GetComparison(left, searchTerm.Operator, right);
// x => x.Property == "Value"
var lambdaExpression = ExpressionHelper
.GetLambda<TEntity, bool>(obj, comparisonExpression);
// query = query.Where...
modifiedQuery = ExpressionHelper.CallWhere(modifiedQuery, lambdaExpression);
}
With the code above and using the below ExpressionHelper class, this generate the below SQL query when I check using SQLProfiler. Please notice the AND in the query. What I actually what is OR.
Constructed QUERY in SQL Profiler
SELECT
[Extent1].[FirstName] AS [FirstName],
FROM [dbo].[tblUser] AS [Extent1]
WHERE ([Extent1].[Conatact1] = N'Bob') AND ([Extent1].[Contact2] = N'John')
ExpressionHelper.cs
public static class ExpressionHelper
{
private static readonly MethodInfo LambdaMethod = typeof(Expression)
.GetMethods()
.First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2);
private static MethodInfo[] QueryableMethods = typeof(Queryable)
.GetMethods()
.ToArray();
private static MethodInfo GetLambdaFuncBuilder(Type source, Type dest)
{
var predicateType = typeof(Func<,>).MakeGenericType(source, dest);
return LambdaMethod.MakeGenericMethod(predicateType);
}
public static PropertyInfo GetPropertyInfo<T>(string name)
=> typeof(T).GetProperties()
.Single(p => p.Name == name);
public static ParameterExpression Parameter<T>()
=> Expression.Parameter(typeof(T));
public static MemberExpression GetPropertyExpression(ParameterExpression obj, PropertyInfo property)
=> Expression.Property(obj, property);
public static LambdaExpression GetLambda<TSource, TDest>(ParameterExpression obj, Expression arg)
=> GetLambda(typeof(TSource), typeof(TDest), obj, arg);
public static LambdaExpression GetLambda(Type source, Type dest, ParameterExpression obj, Expression arg)
{
var lambdaBuilder = GetLambdaFuncBuilder(source, dest);
return (LambdaExpression)lambdaBuilder.Invoke(null, new object[] { arg, new[] { obj } });
}
public static IQueryable<T> CallWhere<T>(IQueryable<T> query, LambdaExpression predicate)
{
var whereMethodBuilder = QueryableMethods
.First(x => x.Name == "Where" && x.GetParameters().Length == 2)
.MakeGenericMethod(new[] { typeof(T) });
return (IQueryable<T>)whereMethodBuilder
.Invoke(null, new object[] { query, predicate });
}
public static IQueryable<TEntity> CallOrderByOrThenBy<TEntity>(
IQueryable<TEntity> modifiedQuery,
bool useThenBy,
bool descending,
Type propertyType,
LambdaExpression keySelector)
{
var methodName = "OrderBy";
if (useThenBy) methodName = "ThenBy";
if (descending) methodName += "Descending";
var method = QueryableMethods
.First(x => x.Name == methodName && x.GetParameters().Length == 2)
.MakeGenericMethod(new[] { typeof(TEntity), propertyType });
return (IQueryable<TEntity>)method.Invoke(null, new object[] { modifiedQuery, keySelector });
}
}
I have hard time understanding on how the query was created and how do I change it to become OR in the created query.
Hope someone can guide me and point to the right direction. Thank you!
Add to SearchTerm a new property (C# 6.0 syntax here):
// This is quite wrong. We should have an enum here, but Operator is
// done as a string, so I'm maintaining the "style"
// Supported LogicalConnector: and, or
public string LogicalConnector { get; set; } = "and";
}
Then:
private static IQueryable<TEntity> BuildQuery<TEntity>(IQueryable<TEntity> modifiedQuery, List<SearchTerm> searchTerms)
{
Expression comparisonExpressions = null;
var obj = ExpressionHelper.Parameter<TEntity>();
foreach (var searchTerm in searchTerms)
{
var propertyInfo = ExpressionHelper
.GetPropertyInfo<TEntity>(searchTerm.EntityName ?? searchTerm.Name);
var left = ExpressionHelper.GetPropertyExpression(obj, propertyInfo);
var right = searchTerm.ExpressionProvider.GetValue(searchTerm.Value);
var comparisonExpression = searchTerm.ExpressionProvider.GetComparison(left, searchTerm.Operator, right);
if (comparisonExpressions == null)
{
comparisonExpressions = comparisonExpression;
}
else if (searchTerm.LogicalConnector == "and")
{
comparisonExpressions = Expression.AndAlso(comparisonExpressions, comparisonExpression);
}
else if (searchTerm.LogicalConnector == "or")
{
comparisonExpressions = Expression.OrElse(comparisonExpressions, comparisonExpression);
}
else
{
throw new NotSupportedException(searchTerm.LogicalConnector);
}
}
if (comparisonExpressions != null)
{
// x => x.Property == "Value"
var lambdaExpression = ExpressionHelper.GetLambda<TEntity, bool>(obj, comparisonExpressions);
// query = query.Where...
modifiedQuery = ExpressionHelper.CallWhere(modifiedQuery, lambdaExpression);
}
return modifiedQuery;
}
Use it like:
var searchTerms = new List<SearchTerm>
{
new SearchTerm { Name = "PrimaryContact", Operator = "eq", Value = "Bob" },
new SearchTerm { Name = "SecondaryContact", Operator = "eq", Value = "Bob" },
new SearchTerm { Name = "PrimaryContact", Operator = "eq", Value = "John", LogicalConnector = "or", }
};
IQueryable<UserEntity> query = BuildQuery<UserEntity>(modifiedQuery, searchTerms);
Note that there is no way in this code to explicitly set brackets, that will be implicitly set as:
(((A opB b) opC C) opD D)
Where A, B, C, D are the SearchTerm[0], SearchTerm[1], SearchTerm[2], SearchTerm[3] and opB, opC, opD are the operators defined in SearchTerm[1].LogicalConnector, SearchTerm[2].LogicalConnector, SearchTerm[3].LogicalConnector.
While putting brackets is easy, choosing how to "describe" them is complex, unless you change significantly your SearchTerm collection (it couldn't be a "linear" array but it would need to be a tree).
P.S. I was wrong, you don't need an ExpressionVisitor. You need an ExpressionVisitor when you are trying to "merge" multiple LambdaExpressions that have distinct ParameterExpression. In this code we are able to have a single var obj = ExpressionHelper.Parameter<TEntity>() for all the query, so no problems merging the conditions. To make it clear: if you want to "merge" x1 => x1.Foo == "Foo1" with x2 => x2.Foo == "Foo2" then you need an ExpressionVisitor that replaces x2 with x1, otherwise you would get a wrong query like x1 => x1.Foo == "Foo1" || x2.Foo == "Foo2". In the code given we have only x1 (that is var obj = ExpressionHelper.Parameter<TEntity>()), so no problem.
When using .Contains()/.StartsWith()/.EndsWith() the generated SQL looks like this :
(( NVL(INSTR(x, y), 0) ) = 1)
Is there a way of using this instead :
LIKE 'x%' or '%x%' or '%x'
Because there is a huge cost difference between those two in the execution plan of the query (44 000 vs 30).
When i look around abit i found LIKE operator in LINQ which were a which have a few good examples of how you could do this. I have tested the one below that was from the link above
Here is a extension for using Like with lambda that was posted by adobrzyc
public static class LinqEx
{
private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains");
private static readonly MethodInfo StartsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
private static readonly MethodInfo EndsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
public static Expression<Func<TSource, bool>> LikeExpression<TSource, TMember>(Expression<Func<TSource, TMember>> property, string value)
{
var param = Expression.Parameter(typeof(TSource), "t");
var propertyInfo = GetPropertyInfo(property);
var member = Expression.Property(param, propertyInfo.Name);
var startWith = value.StartsWith("%");
var endsWith = value.EndsWith("%");
if (startWith)
value = value.Remove(0, 1);
if (endsWith)
value = value.Remove(value.Length - 1, 1);
var constant = Expression.Constant(value);
Expression exp;
if (endsWith && startWith)
{
exp = Expression.Call(member, ContainsMethod, constant);
}
else if (startWith)
{
exp = Expression.Call(member, EndsWithMethod, constant);
}
else if (endsWith)
{
exp = Expression.Call(member, StartsWithMethod, constant);
}
else
{
exp = Expression.Equal(member, constant);
}
return Expression.Lambda<Func<TSource, bool>>(exp, param);
}
public static IQueryable<TSource> Like<TSource, TMember>(this IQueryable<TSource> source, Expression<Func<TSource, TMember>> parameter, string value)
{
return source.Where(LikeExpression(parameter, value));
}
private static PropertyInfo GetPropertyInfo(Expression expression)
{
var lambda = expression as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("expression");
MemberExpression memberExpr = null;
switch (lambda.Body.NodeType)
{
case ExpressionType.Convert:
memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
break;
case ExpressionType.MemberAccess:
memberExpr = lambda.Body as MemberExpression;
break;
}
if (memberExpr == null)
throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");
var output = memberExpr.Member as PropertyInfo;
if (output == null)
throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");
return output;
}
}
To use it you you just simply add Like function where you would put the Contains functions. You can see below for a example
using (CustomerEntities customerContext = new CustomerEntities())
{
IQueryable<Customer> customer = customerContext.Customer.Like(x => x.psn, "%1%");
}
This will create a sql query that looks something like this.
SELECT
[Extent1].[psn] AS [psn]
FROM [dbo].[Customer] AS [Extent1]
WHERE [Extent1].[psn] LIKE '%1%'
I learn how to build simple expression which like
c=>c.code=='XXX';
I create these expression by method below:
public static Expression<Func<T, bool>> BuildStringEqualLambda(string propertyName, string propertyValue)
{
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression propertyExp = parameterExp;
foreach (var property in propertyName.Split('.'))
{
propertyExp = Expression.PropertyOrField(propertyExp, property);
}
Expression right = Expression.Constant(propertyValue);
Expression e1 = Expression.Equal(propertyExp, right);
return Expression.Lambda<Func<T, bool>>(e1, new ParameterExpression[] { parameterExp });
}
I think I can build expression
o=>o.code=='XXX' || c.name=='XXX'
But I don't know how can I build method Any.
Thanks
It should be something like:
public static Expression<Func<T, bool>> BuildStringEqualLambda<T>(params Tuple<string, string>[] propertyNameValues)
{
if (propertyNameValues == null || propertyNameValues.Length == 0)
{
throw new ArgumentException(nameof(propertyNameValues));
}
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression body = null;
foreach (var propertyNameValue in propertyNameValues)
{
Expression propertyExp = parameterExp;
foreach (var property in propertyNameValue.Item1.Split('.'))
{
propertyExp = Expression.PropertyOrField(propertyExp, property);
}
Expression right = Expression.Constant(propertyNameValue.Item2);
Expression eq = Expression.Equal(propertyExp, right);
body = body == null ? eq : Expression.OrElse(body, eq);
}
return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[] { parameterExp });
}
You can probably use some funny LINQ with Aggregate to reduce the number of lines, but it would be undebuggable.
In the end you use the Expression.OrElse (not the Expression.Or that is |!) and you handle the first element case.
Use it like:
var exp = BuildStringEqualLambda(
Tuple.Create("prop1", "value1"),
Tuple.Create("prop2", "value2"),
Tuple.Create("prop3", "value3")
);
Using some LINQ and Aggregate (for those who can't live without LINQing everything) (note that while I wouldn't ever use the LINQed version of the code... It is quite unreadable... Enumerable.Aggregate is "terrible" 😊):
public static Expression<Func<T, bool>> BuildStringEqualLambda<T>(params Tuple<string, string>[] propertyNameValues)
{
if (propertyNameValues == null || propertyNameValues.Length == 0)
{
throw new ArgumentException(nameof(propertyNameValues));
}
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression body = propertyNameValues
.Select(x => BuildEqualityExpression<T>(parameterExp, x.Item1, x.Item2))
.Aggregate((acc, x) => Expression.OrElse(acc, x));
return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[] { parameterExp });
}
private static Expression BuildEqualityExpression<T>(ParameterExpression parameterExp, string propertyName, string propertyValue)
{
Expression propertyExp = propertyName
.Split('.')
.Aggregate((Expression)parameterExp, (acc, x) => Expression.PropertyOrField(acc, x));
Expression right = Expression.Constant(propertyValue);
Expression eq = Expression.Equal(propertyExp, right);
return eq;
}
This is My Expression Class
public static class ExpressionBuilder
{
private static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
public static Expression<Func<T,
bool>> GetExpression<T>(IList<Filter> filters)
{
if (filters.Count == 0)
return null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
Expression exp = null;
if (filters.Count == 1)
exp = GetExpression<T>(param, filters[0]);
else if (filters.Count == 2)
exp = GetExpression<T>(param, filters[0], filters[1]);
else
{
while (filters.Count > 0)
{
var f1 = filters[0];
var f2 = filters[1];
if (exp == null)
exp = GetExpression<T>(param, filters[0], filters[1]);
else
exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1]));
filters.Remove(f1);
filters.Remove(f2);
if (filters.Count == 1)
{
exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0]));
filters.RemoveAt(0);
}
}
}
return Expression.Lambda<Func<T, bool>>(exp, param);
}
private static Expression GetExpression<T>(ParameterExpression param, Filter filter)
{
MemberExpression member = Expression.Property(param, filter.PropertyName);
ConstantExpression constant = Expression.Constant(filter.Value);
switch (filter.Operation)
{
case Op.Equals:
return Expression.Equal(member, Expression.Call(Expression.Convert(Expression.Constant(search.RetrieveGuid), typeof(object)), typeof(object).GetMethod("ToString"))), constant);
case Op.GreaterThan:
return Expression.GreaterThan(member, constant);
case Op.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(member, constant);
case Op.LessThan:
return Expression.LessThan(member, constant);
case Op.LessThanOrEqual:
return Expression.LessThanOrEqual(member, constant);
case Op.Contains:
return Expression.Call(member, containsMethod, constant);
case Op.StartsWith:
return Expression.Call(member, startsWithMethod, constant);
case Op.EndsWith:
return Expression.Call(member, endsWithMethod, constant);
}
return null;
}
private static BinaryExpression GetExpression<T>
(ParameterExpression param, Filter filter1, Filter filter2)
{
Expression bin1 = GetExpression<T>(param, filter1);
Expression bin2 = GetExpression<T>(param, filter2);
return Expression.AndAlso(bin1, bin2);
}
}
when i generate Expression by this code
delegExpFilters = EntityExpression.ExpressionBuilder.GetExpression<Contact>(domainFilter).Compile();
my domainFilter Contains a list records with
Property field name ,operator, and its value and my field is GUID
when i call GetExpression it gives me error
The binary operator Equal is not defined for the types 'System.Guid' and 'System.String'
You are not converting filter value (which is string) to appropriate type:
ConstantExpression constant = Expression.Constant(filter.Value);
Consider you have filter for integer property named Amount:
var filter = new Filter {
PropertyName = "Amount",
Operation = Op.GreaterThan,
Value = "42"
};
Your code will generate expression which equivalent of following code
contract.Amount > "42"
Such integer comparison with string is not allowed.
You should get property type and convert filter value to that type. Here are the steps:
Get type converter for property type
Check whether it can convert string to property type
Do conversion (that will return property value as object
Create cast expression to convert property value from object to property type
Here is code of GetExpression method
var member = Expression.Property(param, filter.PropertyName);
var propertyType = ((PropertyInfo)member.Member).PropertyType;
var converter = TypeDescriptor.GetConverter(propertyType); // 1
if (!converter.CanConvertFrom(typeof(string))) // 2
throw new NotSupportedException();
var propertyValue = converter.ConvertFromInvariantString(filter.Value); // 3
var constant = Expression.Constant(propertyValue);
var valueExpression = Expression.Convert(constant, propertyType); // 4
You should use this value expression instead of your constant expression in binary expressions which you are returning. E.g.:
case Op.LessThan:
return Expression.LessThan(member, valueExpression);
case Op.Equal:
return Expression.Equal(member, valueExpression);
// etc
For equality you should use binary expression as well. And now filter for Amount will be translated into
contract.Amount > (int)42
I wish to sort the collection List<UserClass>
on the basis of one of the property among various of that UserClass which is User Defined.
Is it valid
List<Function> objFunctionsList = new List<Function>();
// populating data in objFunctionsList
objFunctionsList = objFunctionsList.OrderBy(x => x.Name).ToList();
Just use the linq extension method orderby
var sorted=list.OrderBy(x=>x.MyProperty);
If you need a list as a result then add ToList() eg
var sortedList=list.OrderBy(x=>x.MyProperty).ToList();
Alternatively you could use this Extension class
public static class SortExtension {
public static Comparison<T> GetComparer<T, TP>(Expression<Func<T, IComparable<TP>>> propertyExpression) {
if (propertyExpression == null) throw new ArgumentNullException();
var member = ((propertyExpression.Body is UnaryExpression) ? ((UnaryExpression)propertyExpression.Body).Operand : propertyExpression.Body) as MemberExpression;
if (member == null) throw new ArgumentException();
var parameterA = Expression.Parameter(typeof(T), "a");
var parameterB = Expression.Parameter(typeof(T), "b");
var nullExpr = Expression.Constant(null);
var valueA = Expression.Property(parameterA, member.Member.Name);
var valueB = Expression.Property(parameterB, member.Member.Name);
var compare = Expression.Call(valueA, typeof(TP).GetMethod("CompareTo", new[] { typeof(TP) }), valueB);
var checkBPropNull = Expression.Condition(Expression.Equal(valueB, nullExpr), Expression.Constant(0), Expression.Constant(-1));
var checkAPropertyNull = Expression.Condition(Expression.Equal(valueA, nullExpr), checkBPropNull, compare);
var checkBNullANot = Expression.Condition(Expression.Equal(parameterB, nullExpr), Expression.Constant(1), checkAPropertyNull);
var checkBNullANull = Expression.Condition(Expression.Equal(parameterB, nullExpr), Expression.Constant(0), Expression.Constant(-1));
var checkANull = Expression.Condition(Expression.Equal(parameterA, nullExpr), checkBNullANull, checkBNullANot);
return (a, b) => Expression.Lambda<Func<T, T, int>>(checkANull, parameterA, parameterB).Compile()(a, b);
}
public static void SortBy<T, TP>(this List<T> source, Expression<Func<T, IComparable<TP>>> propertyExpression) {
if (source == null) throw new ArgumentNullException();
source.Sort(GetComparer(propertyExpression));
}
}
Then you can just do
list.SortBy(x=>x.MyProperty);
The Expression building produces a comparitor that is functionally equivalent to
list.Sort((a,b) => {
if (a == null) return (b==null) ? 0 :-1;
if (b==null) return 1;
if (a.MyProperty == null) return (b.MyProperty==null) ? 0 : -1;
return a.T1.CompareTo(b.T1);
});
users.Sort((u1, u2) => {
return u1.Age.CompareTo(u2.Age);
});
This will sort by Age for example.
If your list is list, then:
list.Sort((a, b) => {
if (a==null) return (b==null) ? 0 : -1;
if (b==null) return 1;
if (a.Property==null) return (b.Property==null) ? 0 : -1;
return a.Property.CompareTo(b.Property);
});