4

I want to use the following pattern in my controllers:

api/{controller}/{id}/{collection}

Example: api/customers/123/addresses

But I want to return IQueryable Of T from the corresponding Get action. I want something like this (simplified):

public IQueryable<????> GetCollection(int id, string collection)  
{  
    switch(collection)  
    {  
        case "addresses":  
            return _addresses.AsQueryable();  
            break;  
        case "orders":  
            return _orders.AsQueryable();  
            break;  
        default:  
            throw new Exception(NotSupported);  
    }  
}   

Can this be done?
What would be the recommended approach?

3 Answers 3

6

@SLacks is correct that you should return IQueryable<object> or IQueryable<someBaseType> if you can.

The error your getting is a function of the DataContract Serializer. So you have a few options.

  1. Switch to an alternate xml serlializer that supports what you want.
  2. Swtitch to a form of output that bypasses the serializer at issue (say JSON using JSON.net)
  3. Teach the data contract serializer how to serialize your object using the

For the "teach" option, you can teach in two ways.

(A) leverage the [KnownType(typeof(...))] attribute. Here's a post on the KnownType attribute. It's for WCF but should get you started.

(B) use a data contract resolver. This post should get you started.

Sign up to request clarification or add additional context in comments.

Comments

3

Expanding on what @Ebarr said, the easiest way to accomplish this is to update the various classes which you want to be able to return this way, and have them inherit from a common base class or interface.

Example:

[KnownType(typeof(Address))]
[KnownType(typeof(Order))]
public abstract class _BaseObject { }

public partial class Address : _BaseObject { }
public partial class Order : _BaseObject { }

Now you can have your controller method look like:

public IQueryable<_BaseObject> GetCollection(int id, string collection) {  
    switch(collection) {  
        case "addresses":  
            return _addresses.AsQueryable();
        case "orders":  
            return _orders.AsQueryable();
        default:  
            throw new NotSupportedException();  
    }  
}

Note that the [KnownType] attribute is in System.Runtime.Serialization. You should also be aware that this method will result in exactly what you would expect with regards to JSON serialization - but XML serialization will generally result in tags which show your objects as the base class (because that's what you returned) rather than the sub-classes.

Comments

0

Just return the non-generic IQueryable.
Or IQueryable<object> via covariance.

3 Comments

Both of these suggestions fail, I'm afraid. I get an exception in both cases.
1) IQueryable gives: System.Runtime.Serialization.SerializationException: Type 'System.Linq.EnumerableQuery`1[[MvcApplication1.Controllers.Address, MvcApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' with data contract name 'ArrayOfAddress:schemas.datacontract.org/2004/07/MvcApplication1.Controllers' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
2) IQueryable<object> gives: System.Xml.XmlException: You must write an attribute 'type'='object' after writing the attribute with local name '__type'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.