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);
}
}
I have two lists: a list of countries and a list of jobs
public List<Countries> getSharedCountries(string brandName)
{
var items = SharedJobs.Where(a => a.BrandName == brandName);
var items2 = items.OrderBy(a => a.CountryCode);
Countries = new List<Countries>();
string Country = null;
foreach (var item in items2)
{
if (Country != item.CountryCode)
{
Country = item.CountryCode;
Countries.Add(new Countries() { CountryCode = item.CountryCode, JobIDs = getSharedJob(item.CountryCode) });
}
}
return Countries;
}
public void getSharedJob(string Country)
{
var items = SharedJobs.Where(a => a.CountryCode == Country);
JobNetDetails = new List<JobDetail>();
CareerBoardDetails = new List<JobDetail>();
JobSharkDetails = new List<JobDetail>();
JobServeDetails = new List<JobDetail>();
int AusCount = 0;
foreach (var item in items)
{
if (Country == "AUS")
{
AusCount++;
if (AusCount % 4 == 0)
{
JobNetDetails.Add(new JobDetail() { JobPageTitle = item.JobPageTitle, JobID = item.JobID, JobUrl = item.JobUrl });
}
else
{
JobServeDetails.Add(new JobDetail() { JobPageTitle = item.JobPageTitle, JobID = item.JobID, JobUrl = item.JobUrl });
}
}
}
}
On the line where I am accessing the method getSharedJob, it errors and gives me the error, cannot implicitly convert void to system.generic.List?
I am very confused as to why this is happening?
As the signature of your method states, public void getSharedJob(string Country) it's void, so it doesn't return anything, you should change it and return the list you wish.
Edit: As I read in the comments you need to return 4 Lists.
You have several options:
You can return an array of Lists;
You can return a List of Lists;
You can return your own class containing the 4 Lists.
Try below code which returns jobDetails from the method you are calling
public List<Countries> getSharedCountries(string brandName)
{
var items = SharedJobs.Where(a => a.BrandName == brandName);
var items2 = items.OrderBy(a => a.CountryCode);
Countries = new List<Countries>();
string Country = null;
foreach (var item in items2)
{
if (Country != item.CountryCode)
{
Country = item.CountryCode;
foreach (var jobDetail in getSharedJob(item.CountryCode))
{
Countries.Add(new Countries() { CountryCode = item.CountryCode, JobIDs = jobDetail.JobID });
}
}
}
return Countries;
}
public List<JobDetail> getSharedJob(string Country)
{
var items = SharedJobs.Where(a => a.CountryCode == Country);
JobNetDetails = new List<JobDetail>();
CareerBoardDetails = new List<JobDetail>();
JobSharkDetails = new List<JobDetail>();
JobServeDetails = new List<JobDetail>();
int AusCount = 0;
foreach (var item in items)
{
if (Country == "AUS")
{
AusCount++;
if (AusCount % 4 == 0)
{
JobNetDetails.Add(new JobDetail() { JobPageTitle = item.JobPageTitle, JobID = item.JobID, JobUrl = item.JobUrl });
}
else
{
JobServeDetails.Add(new JobDetail() { JobPageTitle = item.JobPageTitle, JobID = item.JobID, JobUrl = item.JobUrl });
}
}
}
return JobServeDetails;
}
Your method signature says take a string variable and return nothing (void).
public void getSharedJob(string country)
JobIDs is expecting a value
JobIDs = getSharedJob(item.CountryCode)
so you need to return a value which matches the JobIDs type which I assume is a List of ints or a List of JobDetails.
When I call 'api/test/name=stop,tap,app...(24 names values)' from the following query below, I am experiencing the an error:
"Message":"An error has occurred.","ExceptionMessage":"Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries.","ExceptionType":"System.Data.SqlClient.SqlException
Linq Query:
var data = db.database_bd.AsQueryable();
if (query.startDate != null)
{
data = data.Where(c => c.UploadDate >= query.startDate);
}
// If any other filters are specified, return records which match any of them:
var filteredData = new List<IQueryable<database_bd>>();
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
foreach (string i in ids)
{
filteredData.Add(data.Where(c => c.Name != null && c.Name.Contains(i)));
}
}
// If no filters passed, return all data.
// Otherwise, combine the individual filters using the Union method
// to return all records which match at least one filter.
if (filteredData.Count != 0)
{
data = filteredData.Aggregate(Queryable.Union);
}
if (!data.Any()) //line causing error
{
var message = string.Format("No data was found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, data);
}
Query class:
public class Query
{
public string name { get; set; }
public Nullable<DateTime> startDate { get; set; }
public Nullable<DateTime> endDate { get; set; }
}
I tried adding a range to the 'filterdata' method, but I could not get that to work. Any advice, would be very much appreciated.
Thanks
This doesn't work?
var data = db.database_bd.AsQueryable();
if (query.startDate != null)
{
data = data.Where(c => c.UploadDate >= query.startDate);
}
if (!string.IsNullOrEmpty(query.name))
{
var list = query.name.Split(',');
data = data.Where(pr => list.Any(pr2 => pr.Name.Contains(pr2)));
}
I would use predicatebuilder for this (look at doc for "installation").
var data = db.database_bd.AsQueryable();
var mainPredicate = PredicateBuilder.True<database_bd>();
if (query.startDate != null)
mainPredicate = mainPredicate.And(c => c.UploadDate >= query.startDate);
if (!string.IsNullOrEmpty(query.name))
{
var namePredicate = PredicateBuilder.False<database_bd>();
var ids = query.name.Split(',');
foreach (var id in ids) {
namePredicate = namePredicate.Or(c => c.Name != null && c.Name.Contains(id));
}
mainPredicate = mainPredicate.And(namePredicate);
}
data = data.Where(mainPredicate );
if (!data.Any()) //line hopefully causing no more error
{
var message = string.Format("No data was found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}