Dynamic lambda expression for where clause - c#

I have a dynamic list of objects on which I am using lambda expression where clause to filter items. For example, just consider that it have 3 properties, foo, bar and baz
class item // let this be current class in dynamic item list
{
bool foo;
string bar;
string baz;
}
Now if I want to filter item list where foo is false I can use following expression
var filtered = itemList.Where("!foo");
I can even filter the list by strings value as
var filtered = itemList.Where("bar==\"value\""); \\all items with bar = value
What I want to actually check is if item in list have a specific string value not null of white space. I tried following code
var filtered = itemList.Where("!String.IsNullOrWhiteSpace(baz)");
It threw an error
Expression of type 'System.Func`2[DynamicType,System.Object]' cannot
be used for parameter of type 'System.String' of method 'Boolean
IsNullOrWhiteSpace(System.String)'
Though I succeeded to get result by following query
var filtered = itemList.Where("baz!=null && baz!=\"\"");
I wanted to confirm if there is a way I can use String.IsNullOrWhiteSpace() in this query.

You can replace "!String.IsNullOrWhiteSpace(baz)" with "!(baz == null || baz.Trim() == string.Empty)" and it should work.

Have a look at System.Linq.Dynamic, there is a great example here.
You will alse need to make sure the List<T> is not List<object>, otherwise System.Linq.Dynamic will not be able to find the properties.
Here is a snippet for your example:
void Main()
{
var itemList = new List<dynamic>{ new {foo = true, bar = "a", baz = "b" }, new {foo = true, bar = (string)null, baz = "d" } };
var filtered = itemList.ToAnonymousList().Where("bar != null and bar !=\"\"");
filtered.Dump();
}
public static class EnumerableEx {
public static IList ToAnonymousList(this IEnumerable enumerable)
{
var enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
throw new Exception("?? No elements??");
var value = enumerator.Current;
var returnList = (IList) typeof (List<>)
.MakeGenericType(value.GetType())
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
returnList.Add(value);
while (enumerator.MoveNext())
returnList.Add(enumerator.Current);
return returnList;
}
}

I have no problem using your expression - it works fine.
I have used this with objects and entities against an EF storage.
Expression of type 'System.Func`2[DynamicType,System.Object]' cannot
be used for parameter of type 'System.String' of method 'Boolean
IsNullOrWhiteSpace(System.String)'
So looking at the error, it is stating (moving the order around):
The method IsNullOrWhiteSpace, that returns a Boolean, expects a parameter of type System.String.
But what was received was Expression of type System.Func``2[DynamicType,System.Object]'
It appears that you may have referenced an object rather than a string value for your comparison. However, without sample code for the objects you are using, and whether you are using objects, entities, or LinQ to SQL (which I haven't tried) we can only guess at what you have supplied for the expression.
Finally, what is a
'dynamic item list'?
Are you using Dynamic, or is it a normal List<Item>?

Related

Regex.IsMatch() in dynamic linq

how to invoke Regex.IsMatch() in IQueryable?
i found the same question but it doesnt work
Invoking Regex.IsMatch() inside a dynamic linq query
first i have
IQueryable Database = data.Verses.Select($"new {{ ID, {TextSearchType.ToString()} }}");
ive tried
var searchResult = Database.Where(Parse());
public static LambdaExpression Parse()
{
ParsingConfig.Default.CustomTypeProvider = new MyCustomTypeProvider();
var options = RegexOptions.IgnoreCase;
string compilableExpression = $"Regex.IsMatch({TextSearchType.ToString()}, \"(^| ){Keyword}($| )\", #0) == true";
ParameterExpression parameter = Expression.Parameter(typeof(Verses));
var DynamicExpression = DynamicExpressionParser.ParseLambda(new[] { parameter },
null,
compilableExpression,
options);
return DynamicExpression;
}
public class MyCustomTypeProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
return new HashSet<Type>
{
typeof(Object),
typeof(Boolean),
typeof(System.Text.RegularExpressions.Regex),
typeof(System.Text.RegularExpressions.RegexOptions),
};
}
}
TextSearchType is property name in Verses class.
im getting this error
No generic method 'Where' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
here is the linq code the im trying to convert to linq dynamic
var rx = new Regex("(^| )" + keyword + "($| )", RegexOptions.IgnoreCase);
var searchResult = Database.AsEnumerable()
.Where(x => rx.IsMatch(x.AyahText)).ToList();
You can not call IsMatch or any other Non-SQL supported functions (see the full list here) in LINQ to Entities.
In order to do what you want, you to have two possible options (maybe more, but I know two at this moment):
Get the items by raw filtering (without using IsMatch), converting the result to List, by calling .ToList() at the end of your query. And then you can filter the collection by Regex.IsMatch.
To be honest, this is not a greater solution, and I wouldn't say I like it.
Create a stored procedure and call it from the c# (official documentation here).

C# list in function param

I'm new to C#. I want to take a list as argument and return another from the data I get from the first one.
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = new List<DestinationGenericMapProps>(datas);
return result;
}
I get this error:
Error 241 The best overloaded method match for System.Collections.Generic.List<VDDataUpdaterGeneric.DataObjects.DestinationGenericMapProps>.List(int) has some invalid arguments
I know this is probably pretty basic but I'm new to C# and struggle with this. Thanks for your help.
List<BoutiqueInWebService> is not a List<DestinationGenericMapProps>.
This will not work unless BoutiqueInWebService is derived from DestinationGenericMapProps.
Basically, there is a List<T>(IEnumerable<T>) constructor, but the T's have to be the same.
Either change your return type to List<BoutiqueInWebService> and change your new statement:
private List<BoutiqueInWebService> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = new List<BoutiqueInWebService>(datas);
return result;
}
or change your parameter to be of type List<DestinationGenericMapProps>:
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<DestinationGenericMapProps> datas)
{
var result = new List<DestinationGenericMapProps>(datas);
return result;
}
Alternatively, if you know how to make a DestinationGenericMapProps from a BoutiqueInWebService, you can use System.Linq and perform a select against the argument:
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = datas.Select(x => new DestinationGenericMapProps() { ... }).ToList();
return result;
}
Your method return type is a list of DestinationGenericMapProps, but you're trying to create list of BoutiqueInWebService (which is data).
You can do this to match your return type:
private List<DestinationGenericMapProps>
ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
return (from d in datas
select new DestinationGenericMapProps()
{
// map properties here
Prop1 = d.SomePropInData
}).ToList();
}
You're getting the error because you're trying to populate a list of one type (DestinationGenericMapProps) with objects from a list of a different type (BoutiqueInWebService) which isn't type safe.
You can only do this if BoutiqueInWebService inherits from DestinationGenericMapProps.
C# supports function overloading, which means that a class can have more than one function with the same name as long as the parameters are different. The compiler decides which overload to call by compairing the types of the parameters. This applies to constructors too.
The List class has a three overloads of its constuctor:
List<T>()
List<T>(IEnumerable<T>)
List<T>(int)
I assume that you are trying to use the second of those as it will create a new list from the passed in one. For the list you are creating T is a DestinationGenericMapProps. So the constructors are:
List<DestinationGenericMapProps>()
List<DestinationGenericMapProps>(IEnumerable<DestinationGenericMapProps>)
List<DestinationGenericMapProps>(int)
The list you have passed in has T set to BoutiqueInWebService. As such the compiler is trying to find a constructor like this in the list above.
List<DestinationGenericMapProps>(IEnumerable<BoutiqueInWebService>)
As it can't find one it raises the error you have recieved.
Is it possible to cast a BoutiqueInWebService to a DestinationGenericMapProps object? If so you could do this:
var result = datas.Cast<DestinationGenericMapProps>().ToList()
If no direct cast is possible it may be possible to do a long hand cast like this:
var result = datas.Select(o => new DestinationGenericMapProps() { PropA = o.PropA, PropB = o.PropB /* etc */}).ToList();

Accessing elements of types with indexers using expression trees

Suppose I have a type like this:
class Context
{
SomeType[] Items { get; set; }
}
I want to be able to access specific Items elements using expression trees. Suppose I need an element at index 0. I can do it like below, where everything works as expected:
var type = typeof (Context);
var param = Expression.Parameter(typeof (object));
var ctxExpr= Expression.Convert(param, context);
var proInfo = type.GetProperty("Items");
Expression.ArrayIndex(Expression.Property(ctxExpr, proInfo), Expression.Constant(0));
If I change the context type to contain .NET provided List<SomeType>, instead of array, i.e.
class Context
{
List<SomeType> Items { get; set; }
}
the same expression results in following error:
System.ArgumentException: Argument must be array at
System.Linq.Expressions.Expression.ArrayIndex(Expression array,
Expression index)
My question is, how to write respective expression which can access an item under appropriate index of List<>, or better - of any collection declaring indexer? E.g. is there is some way to detect, and convert such a collection to an array of appropriate types using expression trees?
An indexer is really just a property with an extra parameter, so you want:
var property = Expression.Property(ctxExpr, proInfo);
var indexed = Expression.Property(property, "Item", Expression.Constant(0));
where "Item" is the name of the indexed property here.
(If you don't know the name of the property beforehand, it's usually Item but you can always find it via reflection, by finding all properties with indexer parameters.)

Entity framework with Linq

I have following method. I need to return var tynames by method so what would be the return type of the method will it be List<string> or something else and also what is the use of FirstOrDefault().
Thanks in advance for your reply
public static List<string> AppType()
{
var context = new Dll_IssueTracking.IssuTrackingEntities();// Object context defined in Dll_IssuTracking DLL
var query = from c in context.ApplicationTypes//Query to find TypeNames
select new { c.TypeName };
var **TypeNames** = query.FirstOrDefault();
}
FirstOrDefault returns the first element found, or the default value (which is null in this case) if the query returned no results.
In this case the return value of the method should be ApplicationType:
public static ApplicationType AppType()
{
var context = new Dll_IssueTracking.IssuTrackingEntities(); // Object context defined in Dll_IssuTracking DLL
var query = from c in context.ApplicationTypes //Query to find TypeNames
select new { c.TypeName };
return query.FirstOrDefault();
}
FirstOrDefault return first element in sequence, in this sample ApplicationTypes is your sequence or a default value if the sequence contains no elements.
FirstOrDefault is an extension method which looks something like this:
public T FirstOrDefault>T>(this IEnumerable<T> query)
{
return query.Any() ? query.First() : default(T);
}
So, it returns the first element in the sequence if it is not empty, or the default of the type if the sequence is empty.
For Instance, if you have an Enumerable<LinqEntity>, then most likely default(LinqEntity) is null. If you had something like Enumerable<int> then default(int) is 0.

How to Filtering List of Objects using LINQ and Return from a Function

I am trying to filter a list of Chord objects (class) in C# using linq.
I have the following function in my class
public List<Chord> FilterDictionary_byKey<Chord>(string theKey)
{
var filteredList = from item in _chords
where item.KeyName == theKey
select item;
return (List<Chord>)filteredList;
}
in the above Chord is an object type, _chords is a class variable of List type.
then from my calling code I tried the following:
List<Chord> theChords = globalVariables.chordDictionary.FilterDictionary_byKey("A");
obviously trying to return a filtered list of Chord objects from the class
however, when i compile the compiler returns
Error 1 The type arguments for method 'ChordDictionary.chordDictionary.FilterDictionary_byKey(string)' cannot be inferred from the usage. Try specifying the type arguments explicitly. C:\Users\Daniel\Development\Projects\Tab Explorer\Tab Explorer\Forms\ScaleAndChordViewer.cs 75 37 Tab Explorer
The inferred type of a from…select LINQ query is actually an IEnumerable<T>, which is not a populated list or collection, but a sequence that will be enumerated on demand. Casting it to List<T> does not cause this enumeration; instead, you should call ToList() or ToArray().
To address the error you’re getting: Your method declaration should not have a generic type parameter, since neither its return type nor any of its parameters are generic. Just remove the <Chord> following the method name.
public List<Chord> FilterDictionary_byKey(string theKey)
{
var filteredList = from item in _chords
where item.KeyName == theKey
select item;
return filteredList.ToList();
}
List<Chord> theChords = globalVariables.chordDictionary.FilterDictionary_byKey<Chord>("A");

Categories