Reputation: 11
I’m programming C# MVC application using JSON and try to retrieve the JSON back to the DB using EF.
Unfortunatly, I connot make such code working:
IEnumerable<object> NewValueSubCollection = (IEnumerable<object>)NewValue.GetType().GetProperty(col.Name).GetValue(NewValue, null);
//Where col is a property of an object of type (IEnumerable<???>)
foreach (var line in ((IEnumerable<object>)col.GetValue(originalEntity, null)).ToList<object>())
{
Type lineType = ObjectContext.GetObjectType(line.GetType());
var lineEntitySet = context.GetEntitySet(lineType);
EntityKey lineKey = context.CreateEntityKey(lineEntitySet.Name, line);
if (NewValueSubCollection.Where(lineKey).Count() == 0) //See bellow
context.DeleteObject(line);
}
Where I’ve implemented this:
public static class LinqExtension
{
//Enable searching in a collection is an entity Key exist... and retrieve info.
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,EntityKey key)
{
return Enumerable.Where(source, CreateFilterExpressionBasedOnKey<T>(key).Compile());
}
private static Expression<Func<TInput, bool>> CreateFilterExpressionBasedOnKey<TInput>(Type sourceType, EntityKey key)
{
var param = Expression.Parameter(sourceType, "");
Expression myFilter = Expression.Constant(true);
if (key != null)
{
foreach (var item in key.EntityKeyValues)
{
Expression Left = Expression.PropertyOrField(param, item.Key.ToString());
Expression Right = Expression.Constant(item.Value);
myFilter = Expression.AndAlso(myFilter, Expression.Equal(Left, Right));
}
}
return Expression.Lambda<Func<TInput, bool>>(myFilter, param);
}
}
The problem is that NewValueSubCollection is of type: IEnumerable<object>
when I wish I could pass IEnumerable<MyListObjectType>
through my Where extension….
Then I’ve got an exception at runtime:
System.ArgumentException: 'TOId' is not a member of type 'System.Object'
If I Use:
Type generic = typeof(Collection<>);
Type typeArgs = (NewValue.GetType().GetProperty(col.Name).GetValue(NewValue, null)).GetType().GetGenericArguments()[0];
Type constructed = generic.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed);
o = NewValue.GetType().GetProperty(col.Name).GetValue(NewValue, null);
I get a compilation error with following code:
if (o.Where(lineKey).Count() == 0) //error!!!
// 'object' does not contain a definition for 'Where' acception a first argument of type 'object' could be found (are you missing directive or an assembly reference?)
Upvotes: 1
Views: 717
Reputation: 1261
In your first example you NewValueSubCollection
is statically type as IEnumerable<object>
, so your parametrized type T
is object
, and when you compile your expression it tries to find the property on the object class, thus the exception.
In the same way your o.Where(...)
cannot work because the Where
extension method is defined on IEnumerable<>
, and the methods are resolved statically at compile time.
I'm afraid you'll have to invoke the Where
method by reflection, you might find some help here: How do I invoke an extension method using reflection?
Upvotes: 0
Reputation: 27515
You'll need to access this dynamically, but you can avoid some of the headache of reflection using the 'dynamic' keyword.
So this:
NewValueSubCollection.Where(lineKey).Count() == 0
Becomes this:
NewValueSubCollection.Cast<dynamic>().Where(d => lineKey(d)).Count() == 0
Or (simpler and more efficient, if enumeration has no side effects) this:
!NewValueSubCollection.Cast<dynamic>().Any(d => lineKey(d))
A simple proof of concept program:
class Program
{
static void Main(string[] args)
{
var list = new List<string>();
IEnumerable<object> objects = list;
Func<string, bool> func = s => s.Contains("x");
list.Add("Test");
list.Add("x");
list.Add("mux");
foreach (var item in objects.Cast<dynamic>().Where(d => func(d)))
Console.WriteLine(item);
Console.ReadKey();
}
}
Upvotes: 1