5

This works:

public IDictionary<int, object> GetProducts( int departmentID )
{
    return new Dictionary<int, object>
                {
                    { 1, new { Description = "Something" } },
                    { 2, new { Description = "Whatever" } },
                };
}

But for some reason this doesn't:

public IDictionary<int, object> GetProducts( int departmentID )
{
    var products = ProductRepository.FindAll( p => p.Department.Id == departmentID );

    return products.ToDictionary( p => p.Id, p => new { Description = p.Description } );
}

This doesn't work either:

public IDictionary<int, object> GetProducts( int departmentID )
{
    var products = ProductRepository.FindAll( p => p.Department.Id == departmentID );

    return products.ToDictionary( p => p.Id, p => new { p.Description } );
}

The compiler error (in both cases) is:

Cannot convert expression type 'System.Collections.Generic.Dictionary<int,{Description:string}>' to return type 'System.Collections.Generic.IDictionary<int,object>'

I assumed it was a problem with the ToDictionary Linq extension method, but according to this answer it should work since FindAll returns an IQueryable<Product>:

... if your data is coming from an IEnumerable or IQueryable source, you can get one using the LINQ ToDictionary operator and projecting out the required key and (anonymously typed) value from the sequence elements:

var intToAnon = sourceSequence.ToDictionary(
    e => e.Id,
    e => new { e.Column, e.Localized });

What gives?

Community
  • 1
  • 1
Big McLargeHuge
  • 14,841
  • 10
  • 80
  • 108
  • 2
    Be aware that you'll have to cast the value to something other than object to do anything useful with it. Since the actual value is an anonymous type, you'll need to resort to reflection or `dynamic`, neither of which are compile-time safe. You would be better off creating a concrete class and using _that_ as the resulting value type. – D Stanley Dec 04 '14 at 18:23
  • Well, the return value is serialized as JSON, which is quite useful. Creating a model just for the purpose of serializing JSON seems redundant to me. – Big McLargeHuge Dec 04 '14 at 18:29
  • True - that is a valid usage. – D Stanley Dec 04 '14 at 18:30

2 Answers2

8

What about explictly casting the dictionary value to object?

return products.ToDictionary( p => p.Id, p => (object)new { Description = p.Description } )

Actually, an anonymous object is an instance of a compile-time randomly-created regular class, thus it's an object but it's of some particular type. This is why you can't expect an implicit cast to IDictionary<string, object>.

Maybe if IDictionary<TKey, TValue> would support covariant TValue...

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
6

It's a bad practice working with anonymous types like you did. Don't try and wrap them as an object. If you need anonymous types, use them at the same method context you're defining them.

What about just changing your method:

public IDictionary<int, object> GetProducts( int departmentID )
{
    return new Dictionary<int, object>
                {
                    { 1, "Something"},
                    { 2, "Whatever"},
                };
}

And then cast the object back into a string?

Of course that's assuming you can't just change the type to IDictionary<int, string>

CodeMonkey
  • 11,196
  • 30
  • 112
  • 203
  • The value actually needs several properties (not just description) such as price, name, color, etc. I only included one property in my example to make it simple. Why is it bad practice? – Big McLargeHuge Dec 04 '14 at 18:19
  • 1
    @Koveras because you cannot cast the object back to the anonymous type. You'll be forced to use `dynamic` or reflection to get at the properties. It's usually better to create classes to put your values in. – juharr Dec 04 '14 at 18:28
  • @Koveras In your case it also sounds legitimate to create an additional Product class to save the data – CodeMonkey Dec 05 '14 at 12:26