I'm trying to allow my entities to be ordered by the Last-Modified property in their metadata, using OData query options.
I tried using a transformer as described in Converting to JSON and accessing metadata, but when I apply ODataQueryOptions
to the resulting IQueryable
, I get an empty array.
The model and view-model:
public class Foo
{
public int Id { get; set; }
}
public class FooViewModel
{
public string Id { get; set; }
public DateTime LastModified { get; set; }
}
The transformer:
public class Foos_WithLastModified : AbstractTransformerCreationTask<Foo>
{
public Foos_WithLastModified()
{
TransformResults = foos => from foo in foos
let metadata = MetadataFor(foo)
select new
{
Id = foo.Id.ToString(CultureInfo.InvariantCulture),
LastModified = metadata.Value<DateTime>("Last-Modified")
};
}
}
The relevant method in FooController
(_session
is an IAsyncDocumentSession
):
public async Task<ICollection<FooViewModel>> Get(ODataQueryOptions<FooViewModel> options)
{
var settings = new ODataValidationSettings();
settings.AllowedOrderByProperties.Add("LastModified");
options.Validate(settings);
var foos = _session.Query<Foo>()
.TransformWith<Foos_WithLastModified, FooViewModel>();
var odataFoos = (IQueryable<FooViewModel>)options.ApplyTo(foos);
return await odataFoos.ToListAsync();
}
When I hit /api/Foo
, the results are as expected:
[
{
"Id": "foos/456",
"LastModified": "2015-11-23T08:43:10.913662Z"
},
{
"Id": "foos/123",
"LastModified": "2015-11-23T08:50:34.0907996Z"
}
]
But when I add OData query options (/api/Foo?$orderby=LastModified
), I get an empty array: []
.
I also tried changing _session
to an IDocumentSession
and modifying Get
as follows,
[EnableQuery(AllowedOrderByProperties = "LastModified")]
public IQueryable<FooViewModel> Get()
{
return _session.Query<Foo>()
.TransformWith<Foos_WithLastModified, FooViewModel>();
}
but I get the same results.
Are transformers the wrong approach? How can I sort by Last-Modified using OData query options?
I do not know how to handle the OData stuff, never tried that, but in order to query for entities, ordered by the metadata value "Last-Modified"
using only RavenDB techniques you can do the following:
Create an index for your entity (in my example a Customer
). In this index we add the field LastModified
that's using the document's metadata value for Last-Modified
.
public class Customer_ByLastModified : AbstractIndexCreationTask<Customer>
{
public class QueryModel
{
public DateTime LastModified { get; set; }
}
public Customer_ByLastModified()
{
Map = customers => from customer in customers
select new
{
LastModified = this.MetadataFor(customer).Value<DateTime>("Last-Modified")
};
}
}
The QueryModel isn't mandatory, but it makes querying via the client API easier, imo. You can then add a Transformer to be able to use the metadata value in your return model:
public class Customers_WithLastModified : AbstractTransformerCreationTask<Customer>
{
public Customers_WithLastModified()
{
TransformResults = results => from customer in results
select new CustomerViewModel
{
Id = customer.Id,
Name = customer.Name,
LastModified = MetadataFor(customer).Value<DateTime>("Last-Modified")
};
}
}
And then query it like this:
using (var session = documentStore.OpenSession())
{
var customers = session.Query<Customer_ByLastModified.QueryModel, Customer_ByLastModified>()
.OrderByDescending(x => x.LastModified)
.TransformWith<Customers_WithLastModified, CustomerViewModel>()
.ToList();
}
Hope this helps!