21

I get the following error when I try to compile my code:

The type arguments for method 'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable, System.Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

        List<RowEntry> entries = ...
        List<RowArgument> arguments = ...

        var argumentsVal = entries.SelectMany((RowEntry entry) =>
            (IEnumerable<RowArgumentVal>)arguments.SelectMany((RowArgument arg) => 
            new RowArgumentVal()
            {
                Action = "X" 
                , EntryID = entry.ID
                , ArgID = arg.ID
                , Val_BIT = true
                , Val_DATE = new DateTime(2014, 01, 04)
                , Val_DATETIME = new DateTime(2014, 01, 04)
                , Val_DECIMAL = 4.6M
                , Val_INT = 88
                , Val_TIME = new TimeSpan(6, 0, 0)
            }
        ).Cast<RowArgumentVal>()).Cast<RowArgumentVal>().ToList();

I don't get how I can "type" this even further...

Random Dev
  • 51,810
  • 9
  • 92
  • 119
Serge Profafilecebook
  • 1,165
  • 1
  • 14
  • 32

4 Answers4

33

The problem is that the inner SelectMany isn't applicable there, and you probably meant Select.

var argumentsVal = entries.SelectMany(entry =>
    arguments.Select(arg => new RowArgumentVal())).ToList();

Each entry will be mapped into an IEnumerable<RowArgumentVal> according to the arguments.

Imagine the outer SelectMany was a simple Select, and it would generate List<IEnumerable<RowArgumentVal>>. But because it is SelectMany, it will "flatten" the result into a simple List<RowArgumentVal>.

The SelectMany method expects a mapping to IEnumerable<T> - not T. Your original code would be valid if RowArgumentVal just happened to implement the IEnumerable<T> interface, which I suppose isn't the case.

SimpleVar
  • 14,044
  • 4
  • 38
  • 60
2

That seems to be a cartesian product from both lists since there is no relation between them. You may want to join them but it's not exactly clear how.

Here is a more readable and compiling way to produce a cartesian product:

var query = from entry in entries
            from argument in arguments
            select new RowArgumentVal
            {
               Action = "X", EntryID = entry.ID,  ArgID = argument.ID, // ...
            };

List<RowArgumentVal> argumentsVal = query.ToList();
prestonsmith
  • 758
  • 1
  • 9
  • 29
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
1

If you want a cartesian product try doing the following

 var argumentsVal = from e in entries
    from a in arguments
    select new RowArgumentVal(...)
Sebastian Piu
  • 7,838
  • 1
  • 32
  • 50
0

As an aside, the "further typing" you could have done would be to give the type arguments to the generic method calls. In particular, if you had changed the second SelectMany to SelectMany<RowArgument, RowArgumentVal> you would have got the errors

Cannot implicitly convert type RowArgumentVal to System.Collections.Generic.IEnumerable<RowArgumentVal>. An explicit conversion exists (are you missing a cast?)

Cannot convert lambda expression to delegate type System.Func<RowArgument,int,System.Collections.Generic.IEnumerable<RowArgumentVal>> because some of the return types in the block are not implicitly convertible to the delegate return type

which would maybe have led you to the other answers here - that you were trying to call a method that expected a sequence, but you were giving it a single object.

(Or in trying to decide which type arguments to add, you would have realised what was wrong sooner.)

Community
  • 1
  • 1
Rawling
  • 49,248
  • 7
  • 89
  • 127