EnableQuery always perform SingleOrDefault on IEnumerable<T> result - odata

I have the following Api Endpoint:
[EnableQuery()]
[Route("rankingreport", Name = "GetRankingReport")]
public IQueryable<RefereeRankingReport> GetRankingReport()
{
var data = (from r in _context.RefereeRankings
join m in _context.Referees on r.RefereeId equals m.ID
join mg in _context.RefereeGradings on r.GradingId equals mg.ID
select new RefereeRankingReport
{
...
})
.AsNoTracking();
return data;
}
I was able to query this endpoint in ASP.NET MVC as such http://url.base/api/referee/rankingreport and got all the results. If needed I then add the odata filters etc on the url to filter the data. http://url.base/api/referee/rankingreport?$filter=GradingId eq 5&$orderby=GradingId,Ranking
However I'm busy upgrading to .net core 3.1 and seeing an exception where it will always return the result as SingleOrDetault. I don't know why it wants to do this as the method is clearly returning IQueryable.
In the startup I have declared this:
services.AddOData();
...
app.UseMvc(routeBuilder =>
{
routeBuilder.EnableDependencyInjection();
routeBuilder.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
});
I don't have the odata route defined, because I don't need or want it.
What am I missing to get this to return the resulting list or records as expected?

In .NET Core 3.1 - Endpoint routing isn't supported (yet), you have to have the OData route there for your functionality to work.

Related

Web API 2 Odata 4 Parameter Issue

I'm having problems defining a function for odata4. The default get would work but I want to require a user parameter so a client set can be determined, other tables are involved so LINQ is required, I also return a DTO instead of the default table info (EF). Below is the code. I get a "Invalid EntitySetPath detected. 'bindingParameter/Client' is not a valid entity set path for procedure 'Default.GetClients'." What am I doing wrong here?
WebApiConfig
public static void Register(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<client>("Client").EntityType.HasKey(p => p.int_id);
var function = builder.Function("GetClients");
function.Parameter<string>("user");
function.ReturnsCollectionFromEntitySet<client>("Client");
builder.EntitySet<ClientDTO>("ClientDTO");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
WebApp.Controller
[ODataRoute("GetClients(user={user})")]
[EnableQuery(PageSize=25)]
public IQueryable<ClientDTO> GetClients([FromODataUri] string user)
{
var clients = (from c in db.clients
join ...
If your OData controller is returning the DTO, the function should look like this:
var function = builder.Function("GetClients");
function.Parameter<string>("user");
function.ReturnsCollectionFromEntitySet<ClientDTO>("Client");
With your current setup, your OData route of GetClients says that it is returning a ClientDTO object, but your WebApiConfig is stating you are returning a Client object.
As the Entity Collection being returned is actually the DTO. The part that shows ("Client") is simply how the OData service will report the name of the object to the project consuming the OData service. For my own personal sanity, I typically include DTO as well so I know when I'm using a DTO and when I'm using a direct entity. So in my own setup i'd return ("ClientDTO"), just a personal preference.

get count from oData enabled web api Get function

i have an oData enabled classic REST web api controller with a Get function like
[EnableQuery()]
public IQueryable<StoreCommand> Get()
{
return _storeCommandService.GetAllStoreCommands().AsQueryable();
}
i need to understand how i can make a call to get only the count of records using some url
i tried
http://localhost:9910/api/storeCommandsrest?$count
but i get
Message: "The query parameter '$count' is not supported."
please note that i am using MongoDb that is returning IQueryable of collection.
If you want to get only the count, try
http://localhost:9910/api/storeCommandsrest/$count

breeze: querying local cache when using client-side model

Consider the below code. It works fine when getting data from the server. I have a custom data adapter (staffManagemetnService) which creates client-side entities from the json returned by the server.
However, if I make a call to executeQueryLocally, it fails and raises the following exception: Cannot find an entityType for resourceName: 'GetInternalResourcesByCompetence'. Consider adding an 'EntityQuery.toType' call to your query or calling the MetadataStore.setEntityTypeForResourceName method to register an entityType for this resourceName
var query = breeze.EntityQuery.from('GetInternalResourcesByCompetence').withParameters(parameters);
var result = self.manager.executeQueryLocally(query.using(dataService.staffManagementService));
if (result) {
return $q.resolve(result);
} else {
return this.manager.executeQuery(query.using(dataService.staffManagementService))
.then(function (data) {
return data.results;
})
.catch(function (err) {
logError('Restrieving resources days off failed', err, true);
});
}
I'm not sure what this means. Should it not work out-of-the-box since I've specifically asked breeze to use the custom dataAdapter ?
It's important to different between resource names and entity type names. Resource names are usually part of an endpoint and in plural (eg orders). Type names are typically singular (eg order).
Locally breeze cannot do much with the resource name, since it won't call the endpoint. Instead you ask for a certain entity type name.
You can map an entityType to a resourcename using the setEntityTypeForResourceName function:
metadataStore.setEntityTypeForResourceName('Speakers', 'Person');
See chapter "Resources names are not EntityType names" and the following chapters here: http://www.getbreezenow.com/documentation/querying-locally

Retrieving entries by key without using filter in Breezejs

When we query for an entity in Breezejs with its key, the framework is creating an url using the $filter property.
/api/orderCollection?$filter=orderId%20eq%20'0001'
Is it possible to force breeze to use the odata format?
/api/orderCollection(0001)
I have a standard odata service and it doesn't support the first url...
this is my query:
var query = new breeze.EntityQuery().from("OrderCollection");
var pred = breeze.Predicate.create('orderId', '==', orderId);
query = query.where(pred);
kr,
Joachim
Breeze always generates OData queries using the $filter operator because that gives us the most flexibility and consistency as you add Predicate expressions. This is part of the OData spec.
However, if you really need the alternative expression you can actually pass the entire URL as a string to Breeze to execute as a query, i.e.
var query = "orderCollection(0001)";
myEntityManager.executeQuery(query).then(function (data) {
...
});
Breeze should still return the same results that this will
var query = new breeze.EntityQuery().from("OrderCollection")
.where('orderId', '==', orderId);
myEntityManager.executeQuery(query).then(function (data) {
...
});

How can I return IQueyrable DTO from Webapi Get so I can use Odata filters

I'm trying to use a Odata filters with ODP.net with Entity framework inside of web api project ASP.NET MVC 4.0 RC. I want to return an IQueryable of OwnDTO . I get an internal 500 error without any details. I know there is an error generation bug with webapi RC, but I dont think that bug is my issue.
Get http://localhost:51744/api/Owner called using Fiddler
[Queryable]
public IQueryable<OwnDTO> Get()
{
using (Entities context = new Entities())
{
var query = from item in context.Owners
select
new OwnDTO
{
Name = item.Name
};
return query.AsQueryable();
}
}
//very simple for example
public class OwnDTO
{
public string Name;
}
I do not want to have use my Oracle EF generated classes (DAO) to return from my Get, but I know I can if I replace EntityObject with a more friendly interface. If I return IEnumerable it works, but I want Odata filters.
Update incase someone wants a working example.. Automapper or simliar should be used in the linq and the context should injected.
[Queryable]
public IQueryable<OwnDTO> Get()
{
{
var query = from item in Hack._EFContext.Owners
select
new OwnDTO
{
Name = item.Name
};
return query.AsQueryable();
}
}
That works fine, but it looks like Odata is removed post RC. So I need to search down another path.
It does work in RC but perhaps not in RTM when it ships - not quite clear yet.
Your problem is that you are disposing your context since you are using a using block. So context get disposed before the data is retrieved.
So instead of using register your object for disposal at the end of request. Tugberk has a post here.

Resources