1

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?)
Julien Ch.
  • 1,231
  • 9
  • 16
Gaugo
  • 11
  • 4
  • 1
    What are the exceptions / compiler errors you get? – sloth Aug 01 '12 at 09:47
  • when working with IEnumerable I've got: – Gaugo Aug 01 '12 at 09:50
  • Exception Details: System.ArgumentException: 'TOId' is not a member of type 'System.Object' when trying to execute the Left Expression and when I use o.Where: '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?) but this error message is 'normal' – Gaugo Aug 01 '12 at 09:52
  • in that case TOId is part of the key of the object... – Gaugo Aug 01 '12 at 10:02
  • The Where Extensions method in on collections and you are trying to call it an object type if (o.Where(lineKey).Count() == 0) //error!!! – HatSoft Aug 01 '12 at 10:05
  • I know but how can I make this work then??? i cannot declare o with var.... otherwise my o.Where doesn't work neither... and eventhough I know it's an IEnumerable I don't know the type of T on advance... – Gaugo Aug 01 '12 at 10:11
  • What version of the framework are you using ? – Julien Ch. Aug 01 '12 at 21:32

2 Answers2

1

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();
    }
}
Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • Unfortunatly, with this Issue, the Type is now a HashSet and even if I redefine the where extension methode to take/retrieve HashSet; at runtime, I get the following error: `Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Collections.Generic.HashSet' does not contain a definition for 'Where'` – Gaugo Aug 02 '12 at 06:37
0

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?

Community
  • 1
  • 1
Julien Ch.
  • 1,231
  • 9
  • 16
  • Thanks for your answer. That's what I was afraid of... My Prime interest to go for extension method was to have a clean code... If I have to call it dynamicly; then advantages to use it (in that context of course) are near to nill... – Gaugo Aug 02 '12 at 06:40