$select and $expand Odata V4 commands are generating an error - select

I am using:
.NET Framework 4.7.2
Visual Studio 2017
OData V4
Web API V 5.2.4 with Entity Framework V6.2 code first with existing MS SQL DB
My $expand and $select commands are generating an error. For example, when I use the select command as follows:
http://localhost:62681/data/Advances?$select=Description
I get the following error:
The query specified in the URI is not valid. Could not find a
property named 'Description' on type
'Microsoft.AspNet.OData.Query.Expressions.SelectSome_1OfAdvance'
$filter and $orderby do work
The odd thing is that this used to work a couple of weeks ago, but when I came back from vacation I could not get it to work. Any help is appreciated.
I've scoured the internet for any clue to my problem, but no luck.
I've upgraded to Microsoft.OData.Core version=7.5.0 and Microsoft.OData.Edm" version="7.5.0
My register method in WebApi.Config does have the line config.Select().Expand().Filter().OrderBy().Count().MaxTop(null);
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
ODataModelBuilder builder = new ODataConventionModelBuilder();
config.Select().Expand().Filter().OrderBy().Count().MaxTop(null);
builder.EntitySet<Advance>("Advances");
builder.EntitySet<Advance_Payments>("Advance_Payments");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "data",
model: builder.GetEdmModel());
config.AddODataQueryFilter(new SecureAccess2Attribute());
}
What am I doing wrong?

I found the solution in another stack overflow posting
EntitySetController $expand and $select not working
Go down about 2/3 of the page and look for a posting that starts with:
I came across a similar issue in OData V4. In this case it turned out if you used an attribute on the Get method and registered another attribute in your config, it errors because you are calling the EnableQuery code twice:

For OData V4, Remove the attribute [EnableQuery] on Controllers/Methods in the controller class IF you have enabled it globally. It accepts it only at one place.

Related

.NET 6 console app how to access configuration and get connection string for DbContext

I am writing a .NET 6 console app and have this unfinished code below. I don't know how to get the connection string from configuration such that I can pass it to the options.UseSqlServer method.
I prefer using the top level statements template.
Also, should I call hostBuilder.Build().Run(); at the end of this code? Or just hostBuilder.Build()? Just wondering what the difference is.
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) =>
{
builder.SetBasePath(Directory.GetCurrentDirectory());
})
.ConfigureServices((context, services) =>
{
services.AddDbContext<CompanyContext>(options => options.UseSqlServer("<connection string from config"));
});
An ASP.NET Core web app is actually a console app that starts an HTTP server. The DI, logging, configuration infrastructure is the same in both cases. The same methods you see in ASP.NET Core tutorials can be used in console applications through the Generic Host Builder.
The Configuration is available through the HostBuilderContext parameter of the ConfigureServices delegate :
.ConfigureServices((context, services) =>
{
var cns=context.Configuration.GetConnectionString("MyConnection");
services.AddDbContext<CompanyContext>(options.UseSqlServer(cns));
});
The WebApplicationBuilder class introduced in .NET (Core) 6.0 still uses the Microsoft.Extensions.Hosting middleware under the hood, but exposes Services, Configuration, Logging etc as properties instead of methods like ConfigureServices to enable top-level and minimal API programs.
I am working on a .NET 6.0 console app with EFCore and I have exactly the same issue. Thanks to your answers I could manage to make the db-scaffolding to work again...
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((config) =>
{
config.AddJsonFile("appsettings.json");
config.AddEnvironmentVariables();
config.Build();
})
.ConfigureServices((context, services) =>
{
var cns = context.Configuration.GetConnectionString("DiBerieBotDB");
services.AddDbContext<DiBerieBotEntities>(options => options.UseSqlServer(cns))
//.AddHostedService<DiBerieBotMain>();
;
})
.Build();
List<User> theUsers = new List<User>();
using (var context = new DiBerieBotEntities())
{
theUsers = (from usr in context.Users.Include("Channels")
select usr).ToList();
}
host.Run();
but on the first Linq query that occurs, the program crashs :
Crash on running Linq query
Also my console app "DiBerieBot.Console" and the DAL "DiBerieBot.DAL" (where the EFCore DbContext class is) are on separate projects :
Projects
Any idea ?

jQuery.sap.includeScript().then() not working in SCP SAPUI5 app

I'm trying to include googlemaps in sapui5
jQuery.sap.includeScript({
url: "https://maps.googleapis.com/maps/api/js?key=XXXX",
promisify: true
}).then(function() { ... } )
This Promise works fine when I run in SAP Web-IDE Trial, but when I deploy it to hana cloud platform it is not working:
InterceptService.js:1 Uncaught (in promise) TypeError: u.indexOf is not a function(…)
sap.ushell.cloudServices.interceptor.InterceptService._invokeFilters # InterceptService.js:1
jQuery.sap.includeScript # InterceptService.js:1
onAfterRendering # Worklist.controller.js:37
InterceptService.js code fragment that produced this error is
{if(u.indexOf('/sap/fiori/../../')>0){u=u.replace('/sap/fiori/../../','/');}
I do use HCP Portal Service to produce HCP Fiori Launchpad Platform.
How to fix this? What I did wrong?
Thanks a lot!
It is indeed issue of InterceptorService, which does not support the syntax of includeScript with object as first argument.
I've forwarded a code of the solution to implementation team of HCP Portal Service and it will be fixed in the next release.
So far, you can achieve the same functionality with the following workaround:
new Promise(function(fnResolve, fnReject) {
jQuery.sap.includeScript(
"https://maps.googleapis.com/maps/api/js?key=XXXX",
"mapsScriptId", fnResolve, fnReject
);
}).then(function() { ... } )
See how UI5 implements it: https://github.com/SAP/openui5/blob/rel-1.38/src/sap.ui.core/src/jquery.sap.global.js#L4387-L4389
Looks like the InterceptService doesn't support the newest signature of jQuery.sap.includeScript (where parameters are provided in a configuration object instead of as individual arguments) yet.
Midterm, the InterceptService needs to be enhanced / fixed. Short-term, you might fall back to the old signature jQuery.sap.includeScript(url, id, onload, onerror). There is unfortunately no way to get a Promise with the old signature.

Query option 'Format' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings

I'm getting an exception on my Web API controller endpoint and I would appreciate some help solving it.
Here is the story:
In my Web API project, a controller exposes the following endpoints:
My Kendo UI Datagrid makes the following request:
http://localhost:63865/api/employees/GetAll?$callback=jQuery21109420544053427875_1410883352953&%24inlinecount=allpages&%24format=json&%24top=5
I'm getting this exception when validating the ODataQueryOptions sent in the request:
Query option 'Format' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings.
But I've enabled all query options:
[EnableQuery(AllowedQueryOptions=AllowedQueryOptions.All)]
What am I doing wrong ?
Finally manage to get to the bottom of this!
After creating the ODataValidationSettings object I needed to change the AllowedQueryOptions to AllowedQueryOptions.All. Be default all options are there except the Format and SkipToken.
Anyway, hope this may help anyone else facing the same issue.
I had the same issue but I had to solve it a different way. My OData service controll was generated using Entity Framework (data first) and it turns out Microsoft's template doesn't support all the OData parameters. This puzzled me because I don't need the extra parameters anyway because I was just trying to define a datasource for use with a TreeView Kendo component. I finally found the Telerik blog post that went into detail about Using Kendo UI With MVC4, WebAPI, OData And EF which focussed on the 'inlinecount' param but for me the culprit was the 'format' param.
I'm pasting the summary from that page here in case the link goes bad.
Remove Inline Count Parameter
transport: {
read: {
url: "/api/Albums", // <-- Get data from here
dataType: "json" // <-- The default was "jsonp"
},
parameterMap: function (options, operation) {
var paramMap = kendo.data.transports.odata.parameterMap(options);
delete paramMap.$inlinecount; // <-- remove inlinecount parameter
delete paramMap.$format; // <-- remove format parameter
return paramMap;
}
}

Performing Explicit Route Mapping based upon Web Api v2 Attributes

I'm upgrading a custom solution where I can dynamically register and unregister Web Api controllers to use the new attribute routing mechanism. However, it seems to recent update to RTM break my solution.
My solution exposes a couple of Web Api controllers for administration purposes. These are registered using the new HttpConfigurationExtensions.MapHttpAttributeRoutes method call.
The solution also allows Web Api controllers to be hosted in third-party assemblies and registered dynamically. At this stage, calling HttpConfigurationExtensions.MapHttAttributeRoutes a second time once the third-party controller is loaded would raise an exception. Therefore, my solution uses reflection to inspect the RoutePrefix and Route attributes and register corresponding routes on the HttpConfiguration object.
Unfortunately, calling the Web Api results in the following error:
"No HTTP resource was found that matches the request URI".
Here is a simple controller that I want to use:
[RoutePrefix("api/ze")]
public sealed class ZeController : ApiController
{
[HttpGet]
[Route("one")]
public string GetOne()
{
return "One";
}
[HttpGet]
[Route("two")]
public string GetTwo()
{
return "Two";
}
[HttpPost]
[Route("one")]
public string SetOne(string value)
{
return String.Empty;
}
}
Here is the first solution I tried:
configuration.Routes.MapHttpRoute("ZeApi", "api/ze/{action}");
Here is the second solution I tried:
var type = typeof(ZeController);
var routeMembers = type.GetMethods().Where(m => m.IsPublic);
foreach (MethodInfo method in routeMembers)
{
var routeAttribute = method.GetCustomAttributes(false).OfType<RouteAttribute>().FirstOrDefault();
if (routeAttribute != null)
{
string controllerName = type.Name.Substring(0, type.Name.LastIndexOf("Controller"));
string routeTemplate = string.Join("/", "api/Ze", routeAttribute.Template);
configuration.Routes.MapHttpRoute(method.Name, routeTemplate);
}
}
I also have tried a third solution, whereby I create custom classes that implement IHttpRoute and trying to register them with the configuration to no avail.
Is it possible to use legacy-style route mapping based upon the information contained in the new routing attributes ?
Update
I have installed my controller in a Web Application in order to troubleshoot the routing selection process with the Web Api Route Debugger. Here is the result of the screenshot:
As you can see, the correct action seems to be selected, but I still get a 404 error.
Update2
After further analysis, and per Kiran Challa's comment below, it seems that the design of Web Api prevents mixing attribute routing and conventional routing, and that what I want to do is not possible using this approach.
I have created a custom attribute [RouteEx] that serves the same purpose of the Web Api [Route] attribute, and now my code works perfectly.
I guess, since this is not possible using the conventional attribute routing, none of the answers on this question could legitimately be consisered valid. So I'm not nominating an answer just yet.
You shouldn't be required to use reflection and inspect the attribute-routing based attributes yourself. Attribute routing uses existing Web API features to get list of controllers to scan through.
Question: Before the switch to attribute routing, how were you loading these assemblies having the
controllers?
If you were doing this by IAssembliesResolver service, then this solution should work even with attribute routing and you should not be needing to do anything extra.
Regarding your Update: are you calling MapHttpAttributeRoutes?

How do I set the default namespaces in MapHttpRoute?

With the standard MapRoute method a can pass a string collection representing the namespaces in which to search for my controller. This seems to have disappeared from MapHttpRoute. How does one define the default namespaces using the new API routing?
We had this problem with the Umbraco core so we created our own IHttpControllerSelector, the source code can be found here:
https://github.com/WebApiContrib/WebAPIContrib/blob/master/src/WebApiContrib/Selectors/NamespaceHttpControllerSelector.cs
You can also install nuget package WebAPIContrib which contains NamespaceHttpControllerSelector.
To register this you can do this on app startup:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration));
The implementation is pretty straight forward and only deals with routes that have the "Namespaces" datatoken set which you have to manually set since the MapHttpRoute doesn't support this. Example:
var r = routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
r.DataTokens["Namespaces"] = new string[] {"Foo"};
The implementation also only caches controllers found with duplicate names since the underlying default implementation removes duplicates from it's cache.
That feature does not exist currently.
Although the feature does not exist at this moment, you can however do this by implementing your own IHttpControllerSelector.
This blog article digs a bit into the details: ASP.NET Web API: Using Namespaces to Version Web APIs
You don't need to set default namespaces with Web API, it will search for controllers in all namespaces in the referenced assemblies (public types with name ending by 'Controller' which implement IHttpController).
Before the MapHttpRoute Factory call add
System.Web.Mvc.ControllerBuilder.Current.DefaultNamespaces.Add("Namespace.Full.Controllers");