Reputation: 24799
IN a OData v4 Controller, is it possible to return different models for the Get()
and the Get([FromIDataUri] key)
?
I like to use ViewModels, and when using the Get()
method I'd like to return an xxxOverviewViewModel
. When using the Get([FromIDataUri] key)
method, I'd like to return a xxxViewModel.
Is this possible, and if so, how?
I've tried to return different models, but I always get a 406 Acceptable
.
Webapi.config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.MapODataServiceRoute("ODataRoute", "odata", GetEdmModel());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Filter().Expand().Select().OrderBy().MaxTop(null).Count();
}
private static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<ComplaintViewModel>("ComplaintOData");
return builder.GetEdmModel();
}
}
ComplaintODataController
public class ComplaintODataController : ODataController
{
private readonly QueryProcessor _queryProcessor;
public ComplaintODataController(QueryProcessor queryProcessor)
{
_queryProcessor = queryProcessor;
}
[EnableQuery]
public IQueryable<ComplaintOverviewViewModel> Get()
{
var result = _queryProcessor.Handle(new GetAllComplaintsQuery());
return result;
}
// WHEN CALLING THIS METHOD I GET A 406:
[EnableQuery]
public ComplaintViewModel Get([FromODataUri] int key)
{
var result = _queryProcessor.Handle(new GetComplaintByIdQuery { Id = key });
return result;
}
}
EDIT:
My GetAllComplaintsQuery.Handle
method looks like this:
public IQueryable<ComplaintOverviewViewModel> Handle(GetAllComplaintsQuery query)
{
// .All is an IQueryable<Complaint>
var result = _unitOfWork.Complaints.All.Select(c => new ComplaintOverviewViewModel
{
ComplaintType = c.ComplaintType.Description,
CreationDate = c.CreationDate,
Customer = c.Customer,
Description = c.Description,
Id = c.Id,
SequenceNumber = c.SequenceNumber,
Creator = c.Creator.Name
});
return result;
}
And this is my ComplaintConfiguration
public class ComplaintConfiguration : EntityTypeConfiguration<Complaint>
{
public ComplaintConfiguration()
{
Property(p => p.SequenceNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
Property(p => p.Description).IsRequired();
}
}
Upvotes: 2
Views: 1879
Reputation: 1480
You did not include definitions for your ViewModel classes, so let me first make sure we're on the same page regarding what you're actually trying to achieve.
It looks like you want to return some restricted set of fields when client requests a list of Complaint records from the parameters-less Get()
but when the client requests a specific complaint from Get([FromODataUri] int key)
method you want to include additional fields.
I modeled this assumption using the following hierarchy:
public class ComplaintTypeViewModel
{
public int Id { get; set; }
}
public class ComplaintOverviewViewModel : ComplaintTypeViewModel
{
public string Name { get; set; }
}
public class ComplaintViewModel : ComplaintOverviewViewModel
{
public string Details { get; set; }
}
Ran tests, GET /odata/ComplaintOData
returned a list with just Id and Name as expected, GET /odata/ComplaintOData(1)
returned a single record containing Details in addition to the other two fields, also as expected.
I never had to change your code for the controller or the WebApiConfig.cs
except the string parameter in builder.EntitySet<ComplaintTypeViewModel>("ComplaintOData");
which had to match the ccontroller (you have "ComplaintTypeOData"
in your code).
Since it worked, I tried to figure out how I can reproduce your 406. I've changed ComplaintViewModel
to extend ComplaintTypeViewModel
directly instead of extending ComplaintOverviewViewModel
(note that I've just duplicated the Name
property):
public class ComplaintTypeViewModel
{
public int Id { get; set; }
}
public class ComplaintOverviewViewModel : ComplaintTypeViewModel
{
public string Name { get; set; }
}
public class ComplaintViewModel : ComplaintTypeViewModel
{
public string Name { get; set; }
public string Details { get; set; }
}
This worked just fine as well.
The only way I could reproduce your 406 is when I changed the ComplaintViewModel
to not have ComplaintTypeViewModel
in its inheritance hierarchy at all:
public class ComplaintTypeViewModel
{
public int Id { get; set; }
}
public class ComplaintOverviewViewModel : ComplaintTypeViewModel
{
public string Name { get; set; }
}
public class ComplaintViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Details { get; set; }
}
This finally gave me the 406 code from GET /odata/ComplaintOData(1)
while GET /odata/ComplaintOData
was still returning the list with Id and Name just fine.
So, it looks like as long as all of your ViewModel classes extend the same T
from builder.EntitySet<T>("name")
you can return any of them from Get()
overloads in your controller. Please check how you define your ViewModels
Upvotes: 1
Reputation: 24599
Try to just remove [EnableQuery]
from action
public ComplaintViewModel Get([FromODataUri] int key)
EnableQueryAttribute
enable querying using the OData query syntax. It is acceptable for collections IQueryable<>
. When you want to return single entity, don't use that attribute.
UPDATES
Also try to rename parameter int key
to id
Upvotes: 0
Reputation: 14389
Create an umbrella class for both models and have your get methods return respectively what you want.
public class myMainModel
{
public xxxOverviewViewModel x {get;set;}
public xxxOverviewViewModel y {get;set;}
}
myMainModel Get()
{
....
return myMainModel.x;
}
myMainModel Get( int key)
{
....
return myMainModel.y;
}
Upvotes: 0