add dynamic anonymous object into linq groupBy expression - c#

I have following linq expression:
Func<Entity, object> groupQuery = item =>
new { a = item.Attributes["name"], item = item.Attributes["number"] };
var result = target.Collection.Entities.GroupBy(groupQuery).ToList();
But if i don't know, how much columns i will group (for example 3 instead of 2),and names of the Attributes stored in List Names, How should i change my groupQuery object? My first idea was to create dynamic object like this but it don't work
dynamic groupQuery= new ExpandoObject();
IDictionary<string, object> dictionary = (IDictionary<string, object>)groupQuery;
foreach (string str in Names)
{
dictionary.Add(str, str);
}

Instead of returning an object from groupQuery you can return a string. This string will be constructed from properties of objects that you want to group. Depending on the configuration it can be generated in different ways i.e. based on different properties. Here is a code that shows an idea:
public class A
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public enum GroupByuMode
{
GroupBy1,
GroupBy2,
GroupBy3,
}
...
var list = new List<A>();
for (int i = 0; i < 10; ++i)
for (int j = 0; j < 10; ++j)
for (int k = 0; k < 10; ++k)
list.Add(new A { Property1 = i.ToString(), Property2 = j.ToString(), Property3 = k.ToString() });
var mode = GroupByuMode.GroupBy1;
Func<A, object> func = a =>
{
if (mode == GroupByuMode.GroupBy1)
return a.Property1;
else if (mode == GroupByuMode.GroupBy2)
return String.Format("{0}_{1}", a.Property1, a.Property2);
else if (mode == GroupByuMode.GroupBy3)
return String.Format("{0}_{1}_{2}", a.Property1, a.Property2, a.Property3);
return null;
};
var res = list.GroupBy(func).ToList();
Console.WriteLine(res.Count);
mode = GroupByuMode.GroupBy2;
res = list.GroupBy(func).ToList();
Console.WriteLine(res.Count);
It works witch LINQ to Objects as shown above. You have to check if it works with LINQ to Entities or another implementation of LINQ.

answer in question
C# LINQ - How to build Group By clause dynamically
IEnumerable<string> columnsToGroupBy = new[] { Names.First()};
Names.RemoveAt(0);
Names.Aggregate(columnsToGroupBy, (current, query) => current.Concat(new[] {query}));
GroupQuery = r => new NTuple<object>(from column in columnsToGroupBy select r[column]);
///////
using System;
using System.Collections.Generic;
using System.Linq;
namespace WBTCB.AggregationService.Models.Helpers
{
public class NTuple<T> : IEquatable<NTuple<T>>
{
public NTuple(IEnumerable<T> values)
{
Values = values.ToArray();
}
public readonly T[] Values;
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
if (obj == null)
return false;
return Equals(obj as NTuple<T>);
}
public bool Equals(NTuple<T> other)
{
if (ReferenceEquals(this, other))
return true;
if (other == null)
return false;
var length = Values.Length;
if (length != other.Values.Length)
return false;
for (var i = 0; i < length; ++i)
if (!Equals(Values[i], other.Values[i]))
return false;
return true;
}
public override int GetHashCode()
{
return Values.Aggregate(17, (current, value) => current*37 + (!ReferenceEquals(value, null) ? value.GetHashCode() : 0));
}
}
}

Related

How to convert array collection to a dataset c#

Hi have a response class that has three models, all of a certain type...
public class AssociationResponse
{
public AssociationModel ItemDetail {get;set;}
public AssociationModel[] Children {get;set;}
public AssociationModel Parent {get; set;}
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
}
Now i am struggling in how to convert the AssociationModel[] Children to a simple dataset, so that i can convert them into datatables and show the results in a tree diagram.. unless anybody knows of a better way?
In my code behind
public AssociationModel ItemDetail { get; set; } = new AssociationModel();
public AssociationModel Parent { get; set; } = new AssociationModel();
public AssociationModel Child { get; set; } = new AssociationModel();
public async Task LoadData(string SerialNumber)
{
try
{
GetAssociationBySerialNumberRequest request = new GetAssociationBySerialNumberRequest()
{
SerialNumber = SerialNumber
};
response = await IAssociation.AssociationGetData(request);
AssociationResponse resp = new AssociationResponse();
if (SerialNumber != null)
{
ItemDetail = response.ItemDetail;
Parent = response.Parent;
**DataSet dset = new DataSet();**
}
}
Any help would be greatful. P.S there are three tables within my [] Children.. So i wanted to somehow access them, i have tried saving to a datatable type, but that doesn't work. any help appreciated.
Edit
The problem, i am having is that i cant seem to convert the arrays to a dataset. Not sure how to do this.
Will this work for you?
I use this extension method to convert IEnumerable to Datatables
public static DataTable ToDataTable<T>(this IList<T> data)
{
var props = TypeDescriptor.GetProperties(typeof(T));
var table = new DataTable();
for (var i = 0; i < props.Count; i++)
{
var prop = props[i];
Type propertyType;
if (prop.PropertyType.IsGenericType &&
prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
propertyType = prop.PropertyType.GetGenericArguments()[0];
}
else
{
propertyType = prop.PropertyType;
}
table.Columns.Add(prop.Name, propertyType);
}
var values = new object[props.Count];
foreach (var item in data)
{
for (var i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}
Edited
What about this method?
public static DataTable ToDataTable(IList<AssociationModel> data)
{
var props = TypeDescriptor.GetProperties(typeof(AssociationModel));
var table = new DataTable();
for (var i = 0; i < props.Count; i++)
{
var prop = props[i];
Type propertyType;
if (prop.PropertyType.IsGenericType &&
prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
propertyType = prop.PropertyType.GetGenericArguments()[0];
}
else
{
propertyType = prop.PropertyType;
}
table.Columns.Add(prop.Name, propertyType);
}
var values = new object[props.Count];
foreach (var item in data)
{
for (var i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}
and your code will change like follow
if (SerialNumber != null)
{
ItemDetail = response.ItemDetail;
Parent = response.Parent;
DataTable dataTable = ToDataTable(response.Children.ToList());
}

Linq Aggregate gives null value rather than expected return List

I'm attempting to utilize linq aggregate in order to mimic a reduce behavior. I need GroupingMethod to return a List of arrays.For some reason GroupingMethod returns null no matter what. I appreciate any help.
List<Group> groupings = GroupingMethod(new int[] {9,6,9,3,5,3,6,6,3,5,5,1,6,9,8});
static List<Group> GroupingMethod(int[] ValueList)
{
int groupTotal = 0;
Group newGroup = new Group();
var put = ValueList.Aggregate(new List<Group>(), (acc, x) =>
{
List<Group> polp = acc;
groupTotal = groupTotal + x;
if (groupTotal < 10)
{
newGroup.AddInt(x);
} else
{
polp.Add(newGroup);
newGroup.Clear();
groupTotal = 0;
}
return polp;
});
return put;
}
class Group
{
List<int> iGrouping = new List<int>();
public Group(int AddInt)
{
iGrouping.Add(AddInt);
}
public Group()
{
}
public void AddInt(int IntToAdd)
{
iGrouping.Add(IntToAdd);
}
public void Clear()
{
iGrouping.Clear();
}
public int CombinedGroupValue { get{return iGrouping.Sum();}}
}

Combine Lambda Expressions using custom operator

I'm implementing a search page where the user can filter the result using a bunch of different filters. I need to pass a Lambda Expression to the search layer in order for the filtering to take place.
My problem is that I don't know how to build a Lambda expression dynamically.
I've tried combining Expressions using AndAlso() but that doesn't work since my Lambda expressions doesn't return a bool.
So I'm guessing I need to implement a ExpressionVisitor and that's where I'm lost right now.
// The custom operator for AFilterType
public static AFilterType operator &(AFilterType first, AFilterType second)
{
return new AndFilter(AFilterType.GetFilters<AndFilter>(first, second));
}
// Here's a simplified version of what I'm trying to do
var filterInput = new FilterInput() { FirstName = "John", LastName = "Doe" };
// Using Match() which is a AFilterType method
Expression<Func<Person, AFilterType>> firstNameFilterExpression = x => x.firstName.Match(filterInput.FirstName);
Expression<Func<Person, AFilterType>> lastNameFilterExpression = x => x.LastName.Match(filterInput.LastName);
// How can I combine those 2 expressions into 1 single Expression at runtime using the custom operator '&' (not the bool '&&').
// Combined Expression should be like this.
Expression<Func<Person, AFilterType>> combinedFilterExpression = x => x.firstName.Match(filterInput.FirstName) & x.LastName.Match(filterInput.LastName);
I've had the same problem once and I solved it using LinqKit and a little bit of reflection (I've used this in EntityFramework project, but it can be adapted to other types, if desired). I will try to post my stripped code below (hope its not too long).
Predisposition: Inclusion of LinqKit (https://www.nuget.org/packages/LinqKit or via NuGet) version 1.1.7.2 or higher.
The code consists of several files in subdirectories:
Interfaces
Framework
Extensions
Interfaces\IPredicateParser.cs
using System;
using System.Collections.Generic;
namespace LambdaSample.Interfaces
{
// Used to defined IPredicateParser for parsing predicates
public interface IPredicateParser
{
bool Parse(string text, bool rangesAllowed, Type definedType);
List<IPredicateItem> Items { get; }
}
}
Interfaces\IPredicateItem.cs
namespace LambdaSample.Interfaces
{
public interface IPredicateItem
{
bool IsValid { get; }
}
}
Framework\PredicateItemSingle.cs
using LambdaSample.Interfaces;
namespace LambdaSample.Framework
{
/// <summary>
/// Item for single predicate (e.g. "44")
/// </summary>
public class PredicateItemSingle : IPredicateItem
{
public PredicateItemSingle()
{
}
public bool IsValid => Value != null;
public object Value { get; set; }
}
}
Framework\PredicateItemRange.cs
using LambdaSample.Interfaces;
namespace LambdaSample.Framework
{
/// <summary>
/// Item for range predicates (e.g. "1-5")
/// </summary>
public class PredicateItemRange : IPredicateItem
{
public PredicateItemRange()
{
}
public bool IsValid => Value1 != null && Value2 != null;
public object Value1 { get; set; }
public object Value2 { get; set; }
}
}
Framework\PredicateParser.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using LambdaSample.Extensions;
using LambdaSample.Interfaces;
namespace LambdaSample.Framework
{
/// <summary>
/// Simple parser for text used in search fields for
/// searching through records or any values
/// </summary>
public class PredicateParser : IPredicateParser
{
private enum RangeType
{
None,
From,
To
}
public PredicateParser()
{
Items = new List<IPredicateItem>();
}
public bool Parse(string text, bool rangesAllowed, Type definedType)
{
Items.Clear();
if (string.IsNullOrWhiteSpace(text))
return true;
var result = true;
var items = text.Split(',');
foreach (var item in items)
{
object val1, val2;
bool isRange;
var ranges = item.Split('-');
if (rangesAllowed && ranges.Length == 2) // Range is only when ranges are allowed and length is 2, otherwise its single value.
{
object val1Temp, val2Temp;
if (ParseValue(ranges[0], definedType, RangeType.From, out isRange, out val1, out val1Temp) &&
ParseValue(ranges[1], definedType, RangeType.To, out isRange, out val2, out val2Temp))
{
Items.Add(new PredicateItemRange { Value1 = val1, Value2 = val2, });
}
else
{
result = false;
}
}
else
{
if (ParseValue(item, definedType, RangeType.None, out isRange, out val1, out val2))
{
if (isRange)
{
Items.Add(new PredicateItemRange { Value1 = val1, Value2 = val2, });
}
else
{
Items.Add(new PredicateItemSingle { Value = val1, });
}
}
else
{
result = false;
}
}
}
return result;
}
private bool ParseValue(string value, Type definedType, RangeType rangeType, out bool isRange, out object result, out object result2)
{
result = null;
result2 = null;
isRange = false;
if (string.IsNullOrWhiteSpace(value))
return false;
// Enums are also treated like ints!
if (definedType == typeof(int) || definedType.IsEnum)
{
int val;
if (!int.TryParse(value, out val))
return false;
result = val;
return true;
}
if (definedType == typeof(long))
{
long val;
if (!long.TryParse(value, out val))
return false;
result = val;
return true;
}
if (definedType == typeof(decimal))
{
decimal val;
if (!decimal.TryParse(value, NumberStyles.Number ^ NumberStyles.AllowThousands, new CultureInfo("sl-SI"), out val))
return false;
result = val;
return true;
}
if (definedType == typeof(DateTime))
{
int year, month, yearMonth;
if (value.Length == 4 && int.TryParse(value, out year) && year >= 1000 && year <= 9999) // If only year, we set whole year's range (e.g. 2015 ==> 2015-01-01 00:00:00.0000000 - 2015-12-31 23:59:59.9999999
{
// Default datetime for From range and if no range
result = new DateTime(year, 1, 1);
switch (rangeType)
{
case RangeType.None:
result2 = ((DateTime)result).AddYears(1).AddMilliseconds(-1);
isRange = true;
break;
case RangeType.To:
result = ((DateTime)result).AddYears(1).AddMilliseconds(-1);
break;
}
return true;
}
if (value.Length == 6 && int.TryParse(value, out yearMonth) && yearMonth >= 100001 && yearMonth <= 999912) // If only year and month, we set whole year's range (e.g. 201502 ==> 2015-02-01 00:00:00.0000000 - 2015-02-28 23:59:59.9999999
{
year = Convert.ToInt32(yearMonth.ToString().Substring(0, 4));
month = Convert.ToInt32(yearMonth.ToString().Substring(4, 2));
// Default datetime for From range and if no range
result = new DateTime(year, month, 1);
switch (rangeType)
{
case RangeType.None:
result2 = ((DateTime)result).AddMonths(1).AddMilliseconds(-1);
isRange = true;
break;
case RangeType.To:
result = ((DateTime)result).AddMonths(1).AddMilliseconds(-1);
break;
}
return true;
}
DateTime val;
if (!value.ParseDateTimeEx(CultureInfo.InvariantCulture, out val))
{
return false;
}
if (val.Hour == 0 && val.Minute == 0)
{
// No hours and minutes specified, searching whole day or to the end of the day.
// If this is no range, we make it a range
result = new DateTime(val.Year, val.Month, val.Day);
switch (rangeType)
{
case RangeType.None:
result2 = ((DateTime)result).AddDays(1).AddMilliseconds(-1);
isRange = true;
break;
case RangeType.To:
result = ((DateTime)result).AddDays(1).AddMilliseconds(-1);
break;
}
return true;
}
result = val;
return true;
}
if (definedType == typeof(string))
{
result = value;
return true;
}
return false;
}
public List<IPredicateItem> Items { get; private set; }
}
}
Extensions\StringExtensions.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace LambdaSample.Extensions
{
public static class StringExtensions
{
private static List<string> GetValidDateTimeFormats()
{
var dateFormats = new[]
{
"dd.MM.yyyy",
"yyyy-MM-dd",
"yyyyMMdd",
}.ToList();
var timeFormats = new[]
{
"HH:mm:ss.fff",
"HH:mm:ss",
"HH:mm",
}.ToList();
var result = (from dateFormat in dateFormats
from timeFormat in timeFormats
select $"{dateFormat} {timeFormat}").ToList();
return result;
}
public static bool ParseDateTimeEx(this string #this, CultureInfo culture, out DateTime dateTime)
{
if (culture == null)
{
culture = CultureInfo.InvariantCulture;
}
if (DateTime.TryParse(#this, culture, DateTimeStyles.None, out dateTime))
return true;
var dateTimeFormats = GetValidDateTimeFormats();
if (DateTime.TryParseExact(#this, dateTimeFormats.ToArray(), culture, DateTimeStyles.None, out dateTime))
return true;
return false;
}
}
}
Extensions\ObjectExtensions.cs
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace LambdaSample.Extensions
{
public static class ObjectExtensions
{
/// <summary>
/// Build Filter Dictionary<string,string> used in ExpressionExtensions.BuildPredicate to build
/// predicates for Predicate Builder based on class's properties values. Filters are then used
/// by PredicateParser, which converts them to appropriate types (DateTime, int, decimal, etc.)
/// </summary>
/// <param name="this">Object to build dictionary from</param>
/// <param name="includeNullValues">Includes null values in dictionary</param>
/// <returns>Dictionary with string keys and string values</returns>
public static Dictionary<string, string> ToFilterDictionary(this object #this, bool includeNullValues)
{
var result = new Dictionary<string, string>();
if (#this == null || !#this.GetType().IsClass)
return result;
// First, generate Dictionary<string, string> from #this by using reflection
var props = #this.GetType().GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(#this);
if (value == null && !includeNullValues)
continue;
// If value already is a dictionary add items from this dictionary
var dictValue = value as IDictionary;
if (dictValue != null)
{
foreach (var key in dictValue.Keys)
{
var valueTemp = dictValue[key];
if (valueTemp == null && !includeNullValues)
continue;
result.Add(key.ToString(), valueTemp != null ? valueTemp.ToString() : null);
}
continue;
}
// If property ends with list, check if list of generics
if (prop.Name.EndsWith("List", false, CultureInfo.InvariantCulture))
{
var propName = prop.Name.Remove(prop.Name.Length - 4, 4);
var sb = new StringBuilder();
var list = value as IEnumerable;
if (list != null)
{
foreach (var item in list)
{
if (item == null)
continue;
if (sb.Length > 0)
sb.Append(",");
sb.Append(item.ToString());
}
result.Add(propName, sb.ToString());
}
continue;
}
var str = value != null ? value.ToString() : null;
result.Add(prop.Name, str);
}
return result;
}
}
}
Extensions\ExpressionExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using LambdaSample.Framework;
using LambdaSample.Interfaces;
using LinqKit;
namespace LambdaSample.Extensions
{
public static class ExpressionExtensions
{
private static readonly MethodInfo StringContainsMethod = typeof(string).GetMethod(#"Contains", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null);
private static readonly MethodInfo StringStartsWithMethod = typeof(string).GetMethod(#"StartsWith", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null);
private static readonly MethodInfo StringEndsWithMethod = typeof(string).GetMethod(#"EndsWith", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null);
private static readonly MethodInfo ObjectEquals = typeof(object).GetMethod(#"Equals", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(object) }, null);
//private static readonly MethodInfo BooleanEqualsMethod = typeof(bool).GetMethod(#"Equals", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(bool) }, null);
/// <summary>
/// Build a predicate with linq clauses, taking searchCriteria object's properties to define where conditions.
/// </summary>
/// <typeparam name="TDbType">Type of entity to build predicate for</typeparam>
/// <param name="searchCriteria">Object which contains criteria for predicate</param>
/// <param name="predicateParser">Implementation of predicate parser that will parse predicates as string</param>
/// <param name="includeNullValues">Determines whether null values are included when constructing query</param>
/// <returns></returns>
public static Expression<Func<TDbType, bool>> BuildPredicate<TDbType>(object searchCriteria, IPredicateParser predicateParser, bool includeNullValues)
{
var filterDictionary = searchCriteria.ToFilterDictionary(includeNullValues);
return BuildPredicate<TDbType>(filterDictionary, predicateParser);
}
public static Expression<Func<TDbType, bool>> BuildPredicate<TDbType>(Dictionary<string, string> searchCriteria, IPredicateParser predicateParser)
{
var predicateOuter = PredicateBuilder.New<TDbType>(true);
var predicateErrorFields = new List<string>();
var dict = searchCriteria;// as Dictionary<string, string>;
if (dict == null || !dict.Any())
return predicateOuter;
var searchFields = typeof(TDbType).GetProperties();
foreach (var searchField in searchFields)
{
// Get the name of the DB field, which may not be the same as the property name.
var dbFieldName = GetDbFieldName(searchField);
var dbType = typeof(TDbType);
var dbFieldMemberInfo = dbType.GetMember(dbFieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).SingleOrDefault();
if (dbFieldMemberInfo == null || !dict.ContainsKey(dbFieldMemberInfo.Name))
continue;
var predicateValue = dict[dbFieldMemberInfo.Name];
if (predicateValue == null)
continue;
var rangesAllowed = searchField.PropertyType != typeof(string);
if (!predicateParser.Parse(predicateValue, rangesAllowed, searchField.PropertyType))
{
predicateErrorFields.Add(dbFieldMemberInfo.Name);
continue;
}
if (!predicateParser.Items.Any())
continue;
var predicateInner = BuildInnerPredicate<TDbType>(predicateParser, searchField, dbFieldMemberInfo);
if (predicateInner == null)
continue;
predicateOuter = predicateOuter.And(predicateInner);
}
return predicateOuter;
}
private static Expression<Func<TDbType, bool>> BuildInnerPredicate<TDbType>(IPredicateParser predicateParser, PropertyInfo searchField, MemberInfo dbFieldMemberInfo)
{
var dbType = typeof(TDbType);
// Create an "x" as TDbType
var dbTypeParameter = Expression.Parameter(dbType, #"x");
// Get at x.firstName
var dbFieldMember = Expression.MakeMemberAccess(dbTypeParameter, dbFieldMemberInfo);
Expression<Func<TDbType, bool>> predicateInner = null;
foreach (var predicateItem in predicateParser.Items)
{
var predicateItemSingle = predicateItem as PredicateItemSingle;
var predicateItemRange = predicateItem as PredicateItemRange;
if (predicateItemSingle != null)
{
// Create the MethodCallExpression like x.firstName.Contains(criterion)
if (searchField.PropertyType == typeof(string))
{
var str = predicateItemSingle.Value as string ?? "";
var startsWithAsterisk = str.StartsWith("*");
var endsWithAsterisk = str.EndsWith("*");
str = str.Trim('*').Trim();
MethodCallExpression callExpression;
if (startsWithAsterisk && !endsWithAsterisk)
{
callExpression = Expression.Call(dbFieldMember, StringEndsWithMethod, new Expression[] { Expression.Constant(str) });
}
else if (!startsWithAsterisk && endsWithAsterisk)
{
callExpression = Expression.Call(dbFieldMember, StringStartsWithMethod, new Expression[] { Expression.Constant(str) });
}
else
{
callExpression = Expression.Call(dbFieldMember, StringContainsMethod, new Expression[] { Expression.Constant(str) });
}
predicateInner = (predicateInner ?? PredicateBuilder.New<TDbType>(false)).Or(Expression.Lambda(callExpression, dbTypeParameter) as Expression<Func<TDbType, bool>>);
}
else
{
if (dbFieldMember.Type.IsEnum)
{
if (!dbFieldMember.Type.IsEnumDefined(predicateItemSingle.Value))
continue;
var enumValue = (int)predicateItemSingle.Value;
if (enumValue <= 0)
continue;
var enumObj = Enum.ToObject(dbFieldMember.Type, (int)predicateItemSingle.Value);
predicateInner = (predicateInner ?? PredicateBuilder.New<TDbType>(false)).Or(Expression.Lambda<Func<TDbType, bool>>(Expression.Equal(dbFieldMember, Expression.Constant(enumObj)), new[] { dbTypeParameter }));
}
else
{
predicateInner = (predicateInner ?? PredicateBuilder.New<TDbType>(false)).Or(Expression.Lambda<Func<TDbType, bool>>(Expression.Equal(dbFieldMember, Expression.Constant(predicateItemSingle.Value)), new[] { dbTypeParameter }));
}
}
}
else if (predicateItemRange != null)
{
var predicateRange = PredicateBuilder.New<TDbType>(true);
predicateRange = predicateRange.And(Expression.Lambda<Func<TDbType, bool>>(Expression.GreaterThanOrEqual(dbFieldMember, Expression.Constant(predicateItemRange.Value1)), new[] { dbTypeParameter }));
predicateRange = predicateRange.And(Expression.Lambda<Func<TDbType, bool>>(Expression.LessThanOrEqual(dbFieldMember, Expression.Constant(predicateItemRange.Value2)), new[] { dbTypeParameter }));
predicateInner = (predicateInner ?? PredicateBuilder.New<TDbType>(false)).Or(predicateRange);
}
}
return predicateInner;
}
private static string GetDbFieldName(PropertyInfo propertyInfo)
{
var dbFieldName = propertyInfo.Name;
// TODO: Can put custom logic here, to obtain another field name if desired.
return dbFieldName;
}
}
}
Usage
Let's say we have the DbPerson class that holds our data:
public class DbPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public int Age { get; set; }
}
And beside that DbPerson class we have a class that represents our filter for the DbPerson objects:
public class DbPersonFilter
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string BirthDate { get; set; }
public string Age { get; set; }
}
Notice how the names of properties for base class DbPerson and DbPersonFilter are the same. This is important because a lot of code above requires that naming convention is consistent. Type of properties, however are not the same. This is because for filter we can set ranges to search, not just one value. Later there will be some samples to see how this works.
Now, let us fill our "database" with simple data. We use this method:
private List<DbPerson> GenerateTestDb()
{
var result = new List<DbPerson>
{
new DbPerson { Id = 1,FirstName = "John", LastName = "Doe", BirthDate = new DateTime(1963, 6, 14), Age = 53 },
new DbPerson { Id = 2,FirstName = "Jane", LastName = "Hunt", BirthDate = new DateTime(1972, 1, 16), Age = 44 },
new DbPerson { Id = 3,FirstName = "Aaron", LastName = "Pitch", BirthDate = new DateTime(1966, 7, 31), Age = 50 },
};
return result;
}
Using clauses for our sample applications are the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using LambdaSample.Extensions;
using LambdaSample.Framework;
using LinqKit;
Now, lets create some btnTest in our WinForms application (of course, you would you this in your app, whatever may be):
private void btnTest_Click(object sender, EventArgs e)
{
// Load sample database into db (db is actually List<DbPerson>)
var db = GenerateTestDb();
// Create filter looking for FirstName is "John"
var filterValues = new DbPersonFilter
{
FirstName = "John",
};
// Build PredicateParser which it used to parse predicates inside ExpressionExtensions.
var predicateParser = new PredicateParser();
// Build predicate...
var predicate1 = PredicateBuilder.New(ExpressionExtensions.BuildPredicate<DbPerson>(filterValues, predicateParser, true));
// And search for items...
var items1 = db.AsQueryable().AsExpandable().Where(predicate1).ToList();
// Create filter to look for items where Id is between 1 and 2
filterValues = new DbPersonFilter
{
Id = "1-2",
};
// Build predicate...
var predicate2 = PredicateBuilder.New(ExpressionExtensions.BuildPredicate<DbPerson>(filterValues, predicateParser, true));
// And search for items...
var items2 = db.AsQueryable().AsExpandable().Where(predicate2).ToList();
// Create filter to look for items where Age is 44
filterValues = new DbPersonFilter
{
Age = "44",
};
// Build predicate...
var predicate3 = PredicateBuilder.New(ExpressionExtensions.BuildPredicate<DbPerson>(filterValues, predicateParser, true));
// And search for items...
var items3 = db.AsQueryable().AsExpandable().Where(predicate3).ToList();
}
Hope this helps. Code should be self-explanatory, since comments are not included everywhere. If you have any more questions, do ask.
NOTE: .AsExpandable() is and extension method of LinqKit, to use PredicateBuilder inside Where extension method.
I deliberatly left out details about the actual domain in which I'm working. This was an attempt to make my question more generic and focused on Expressions.
But as it turned out there was a FilterExpressionParser available in the API I'm using (Episerver FIND) which came in handy.
So here's a function that builds and applies a composite filter.
private void MechanicalPropertiesFilter(SteelNavigatorForm form, ref ITypeSearch<SteelGradeVariantPage> search)
{
FilterExpressionParser filterExpressionParser = new FilterExpressionParser(SearchClient.Instance.Conventions);
Filter combinedFilter = null;
// Dimension
if (form.DimensionThickness > 0)
{
var dimensionFilter = filterExpressionParser.GetFilter<MechanicalProperties>(m => m.DimensionInMillimeterMin.LessThan(form.DimensionThickness)
& m.DimensionInMillimeterMax.GreaterThan(form.DimensionThickness));
combinedFilter = (combinedFilter == null) ? dimensionFilter : combinedFilter & dimensionFilter;
}
// Yield strength
if (form.YieldStrengthMin > 0)
{
var yieldStrengthFilter = filterExpressionParser.GetFilter<MechanicalProperties>(m => m.YieldStrengh.GreaterThan(form.YieldStrengthMin));
combinedFilter = (combinedFilter == null) ? yieldStrengthFilter : combinedFilter & yieldStrengthFilter;
}
// Tensile strength
if (form.TensileStrengthMin > 0 | form.TensileStrengthMax > 0)
{
var tensileStrengthMin = (form.TensileStrengthMin == 0) ? double.MinValue : form.TensileStrengthMin;
var tensileStrengthMax = (form.TensileStrengthMax == 0) ? double.MaxValue : form.TensileStrengthMax;
var tensileStrengthFilter = filterExpressionParser.GetFilter<MechanicalProperties>(m => m.TensileStrengthMin.InRangeInclusive(tensileStrengthMin, tensileStrengthMax) | m.TensileStrengthMax.InRangeInclusive(tensileStrengthMin, tensileStrengthMax));
combinedFilter = (combinedFilter == null) ? tensileStrengthFilter : combinedFilter & tensileStrengthFilter;
}
// Elongation
if (form.Elongation > 0)
{
var elongationFilter = filterExpressionParser.GetFilter<MechanicalProperties>(m => m.ElongationA5Percentage.GreaterThan(form.Elongation));
combinedFilter = (combinedFilter == null) ? elongationFilter : combinedFilter & elongationFilter;
}
// Hardness
if (form.HardnessMin > 0 || form.HardnessMax > 0)
{
var max = (form.HardnessMax == 0) ? double.MaxValue : form.HardnessMax;
var hardnessFilter = filterExpressionParser.GetFilter<MechanicalProperties>(m => m.HardnessScaleGuid.Match(form.HardnessMethod) & (
m.HardnessMin.InRangeInclusive(form.HardnessMin, max)
| m.HardnessMax.InRangeInclusive(form.HardnessMin, max)));
combinedFilter = (combinedFilter == null) ? hardnessFilter : combinedFilter & hardnessFilter;
}
if (combinedFilter != null)
{
NestedFilterExpression<SteelGradeVariantPage, MechanicalProperties> mechanicalFilterExpression = new NestedFilterExpression<SteelGradeVariantPage, MechanicalProperties>(v => v.MechanicalProperties, ((MechanicalProperties item) => combinedFilter), search.Client.Conventions);
search = search.Filter(mechanicalFilterExpression.NestedFilter);
}
}

How to implement a equality method?

Given the examples below, how would I make clientList contain 5 clients in the second example?
I want the list.Contains() method only check the FName and LName strings and disregard the age when checking for equality.
struct client
{
public string FName{get;set;}
public string LName{get;set;}
public int age{get;set;}
}
Example 1:
List<client> clientList = new List<client>();
for (int i = 0; i < 5; i++)
{
client c = new client();
c.FName = "John";
c.LName = "Smith";
c.age = 10;
if (!clientList.Contains(c))
{
clientList.Add(c);
}
}
//clientList.Count(); = 1
Example 2:
List<client> clientList = new List<client>();
for (int i = 0; i < 5; i++)
{
client c = new client();
c.FName = "John";
c.LName = "Smith";
c.age = i;
if (!clientList.Contains(c))
{
clientList.Add(c);
}
}
//clientList.Count(); = 5
Create a Class which implements IEqualityComparer , and pass the object in list.contains method
public class Client : IEquatable<Client>
{
public string PropertyToCompare;
public bool Equals(Client other)
{
return other.PropertyToCompare == this.PropertyToCompare;
}
}
Override Equals and GetHashCode in your structure:
struct client
{
public string FName { get; set; }
public string LName { get; set; }
public int age { get; set; }
public override bool Equals(object obj)
{
if (obj == null || !(obj is client))
return false;
client c = (client)obj;
return
(string.Compare(FName, c.FName) == 0) &&
(string.Compare(LName, c.LName) == 0);
}
public override int GetHashCode()
{
if (FName == null)
{
if (LName == null)
return 0;
else
return LName.GetHashCode();
}
else if (LName == null)
return FName.GetHashCode();
else
return FName.GetHashCode() ^ LName.GetHashCode();
}
}
This implementation handles all the edge cases.
Read this question to know why you should also override GetHashCode().
Assuming you are using C# 3.0 or greater, try something like this:
(The following code is not tested, but should be about right)
List<client> clientList = new List<client>();
for (int i = 0; i < 5; i++)
{
client c = new client();
c.FName = "John";
c.FName = "Smith";
c.age = i;
var b = (from cl in clientList
where cl.FName = c.FName &&
cl.LName = c.LName
select cl).ToList().Count() <= 0;
if (b)
{
clientList.Add(c);
}
}

Tarjan cycle detection help C#

Here is a working C# implementation of tarjan's cycle detection.
The algorithm is found here:
http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
public class TarjanCycleDetect
{
private static List<List<Vertex>> StronglyConnectedComponents;
private static Stack<Vertex> S;
private static int index;
private static DepGraph dg;
public static List<List<Vertex>> DetectCycle(DepGraph g)
{
StronglyConnectedComponents = new List<List<Vertex>>();
index = 0;
S = new Stack<Vertex>();
dg = g;
foreach (Vertex v in g.vertices)
{
if (v.index < 0)
{
strongconnect(v);
}
}
return StronglyConnectedComponents;
}
private static void strongconnect(Vertex v)
{
v.index = index;
v.lowlink = index;
index++;
S.Push(v);
foreach (Vertex w in v.dependencies)
{
if (w.index < 0)
{
strongconnect(w);
v.lowlink = Math.Min(v.lowlink, w.lowlink);
}
else if (S.Contains(w))
{
v.lowlink = Math.Min(v.lowlink, w.index);
}
}
if (v.lowlink == v.index)
{
List<Vertex> scc = new List<Vertex>();
Vertex w;
do
{
w = S.Pop();
scc.Add(w);
} while (v != w);
StronglyConnectedComponents.Add(scc);
}
}
Note a DepGraph is just a list of Vertex. and Vertex has a list of other Vertex which represent the edges. Also index and lowlink are initialized to -1
EDIT: This is working...I just misinterpreted the results.
The above is actually correct, I did not understand what a strongly connected component was. I was expecting the function to return an empty List of strongly connected components, yet it was returning a list of single nodes.
I believe the above is working. Feel free to use if you need it!
As of 2008 quickgraph has supported this algorithm. See the StronglyConnectedComponentsAlgorithm class for the implementation, or AlgorithmExtensions.StronglyConnectedComponents method for a usage shortcut.
Example:
// Initialize result dictionary
IDictionary<string, int> comps = new Dictionary<string, int>();
// Run the algorithm
graph.StronglyConnectedComponents(out comps);
// Group and filter the dictionary
var cycles = comps
.GroupBy(x => x.Value, x => x.Key)
.Where(x => x.Count() > 1)
.Select(x => x.ToList())
Example presented above in question isn't functional should anyone want to quickly play with it. Also note that it is stack based, which will detonate your stack if you give anything but the most trivial of graphs. Here is a working example with a unit test that models the graph presented on the Tarjan wikipedia page:
public class Vertex
{
public int Id { get;set; }
public int Index { get; set; }
public int Lowlink { get; set; }
public HashSet<Vertex> Dependencies { get; set; }
public Vertex()
{
Id = -1;
Index = -1;
Lowlink = -1;
Dependencies = new HashSet<Vertex>();
}
public override string ToString()
{
return string.Format("Vertex Id {0}", Id);
}
public override int GetHashCode()
{
return Id;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
Vertex other = obj as Vertex;
if (other == null)
return false;
return Id == other.Id;
}
}
public class TarjanCycleDetectStack
{
protected List<List<Vertex>> _StronglyConnectedComponents;
protected Stack<Vertex> _Stack;
protected int _Index;
public List<List<Vertex>> DetectCycle(List<Vertex> graph_nodes)
{
_StronglyConnectedComponents = new List<List<Vertex>>();
_Index = 0;
_Stack = new Stack<Vertex>();
foreach (Vertex v in graph_nodes)
{
if (v.Index < 0)
{
StronglyConnect(v);
}
}
return _StronglyConnectedComponents;
}
private void StronglyConnect(Vertex v)
{
v.Index = _Index;
v.Lowlink = _Index;
_Index++;
_Stack.Push(v);
foreach (Vertex w in v.Dependencies)
{
if (w.Index < 0)
{
StronglyConnect(w);
v.Lowlink = Math.Min(v.Lowlink, w.Lowlink);
}
else if (_Stack.Contains(w))
{
v.Lowlink = Math.Min(v.Lowlink, w.Index);
}
}
if (v.Lowlink == v.Index)
{
List<Vertex> cycle = new List<Vertex>();
Vertex w;
do
{
w = _Stack.Pop();
cycle.Add(w);
} while (v != w);
_StronglyConnectedComponents.Add(cycle);
}
}
}
[TestMethod()]
public void TarjanStackTest()
{
// tests simple model presented on https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
var graph_nodes = new List<Vertex>();
var v1 = new Vertex() { Id = 1 };
var v2 = new Vertex() { Id = 2 };
var v3 = new Vertex() { Id = 3 };
var v4 = new Vertex() { Id = 4 };
var v5 = new Vertex() { Id = 5 };
var v6 = new Vertex() { Id = 6 };
var v7 = new Vertex() { Id = 7 };
var v8 = new Vertex() { Id = 8 };
v1.Dependencies.Add(v2);
v2.Dependencies.Add(v3);
v3.Dependencies.Add(v1);
v4.Dependencies.Add(v3);
v4.Dependencies.Add(v5);
v5.Dependencies.Add(v4);
v5.Dependencies.Add(v6);
v6.Dependencies.Add(v3);
v6.Dependencies.Add(v7);
v7.Dependencies.Add(v6);
v8.Dependencies.Add(v7);
v8.Dependencies.Add(v5);
v8.Dependencies.Add(v8);
graph_nodes.Add(v1);
graph_nodes.Add(v2);
graph_nodes.Add(v3);
graph_nodes.Add(v4);
graph_nodes.Add(v5);
graph_nodes.Add(v6);
graph_nodes.Add(v7);
graph_nodes.Add(v8);
var tcd = new TarjanCycleDetectStack();
var cycle_list = tcd.DetectCycle(graph_nodes);
Assert.IsTrue(cycle_list.Count == 4);
}
I added a Id property to the Vertex object so it is simple to see what is being done, it isn't strictly needed. I also cleaned up some of the code a little, author was using naming from page pseudo-code, which is good for comparison, but it wasn't very informative.

Categories

Resources