.net Core 2.0 OData - EnableQuery attribute not allowing operations - asp.net-core

I am trying to use Microsoft.AspNetCore.OData, v7.0.0.0-beta1, in a simple project.
I am failing to filter, select, and use top or skip.
Overriding the ValidateQuery of the EnableQueryAttribute, I can successfully perform these type of operations so I believe the library is buggy.
I could not find the right channel to report the issue, can anyone help?
The sample code to reproduce is available here: https://github.com/norcino/AspNetCoreSamples/tree/master/SampleODataApp

The solution to the problem was the actual initialization of the MVC route builder.
Like in the .net framework version it is possible to specify which operation is allowed for OData query. In the example below I am whitelisting everything but you can do a more fine tuning passing an instance of QueryOptionSetting.
app.UseMvc(routeBuilder =>
{
routeBuilder
.Select()
.Expand()
.Filter()
.OrderBy(QueryOptionSetting.Allowed)
.MaxTop(2000)
.Count();
routeBuilder.EnableDependencyInjection();
});
Note that, the attribute [HttpGet, EnableQuery(AllowedOrderByProperties = "Id")], will effectively enforce the order by to the sole Id property specified in the attribute, but first you need to enable all from the configuration.

Related

How to register two controllers same name, different namespace in same ODATA EDM Model?

Objective
The objective is to better understand how to develop modular (plugins by 3rd parties) approach to developing APIs, on top of ASP.NET Core.
To make plugin development easy to pick up it should rely on Conventions where possible, and deliver high value.
OData's queryability and being an Standard remains a compelling improvement to REST, providing a lot of bang for effort.
Issues
OData may be powerful, but the availability of current documentation remains sub par, hence unsure of its limits/capabilities.
Hence questions regarding what options one has to untangling routing issues due to conflicting endpoints from controllers in 3rd party modules.
Specifically:
Q1: Can a single OData EDM model disambiguate between two Controllers with the same name in two different namespaces?
Q2: Can one register an ODataController with a different Route than the ODataController name (eg route Foo points to BarController, when the convention is it would look for a FooController) without breaking default functionality? (eg: $count stops working for me)
Q2b: Even if we take over parsing incoming Uris into its odata path components as well as the logic it uses to find the relevant Controller?
Q3: If it can't, would the more practical approach to loading Plugins be for each Plugin to register its own EDM models?
Q4: Will using multiple EDM models work even if the second plugin/edm model has Controllers that exposes Models that has properties that refer back to Models exposed by Controllers in the base (or another dependency Plugin) EDM model?
Eg: Base EDM exposes Persons and Addresses, and second plugin EDM exposes maybe Customers, which has properties referencing Person and Addresses types, provided by the base EDM?
(I'm guessing it will work, but not 100% sure...anybody see an issue with this?)
Q5: How can one add new EDM models dynamically, resetting the routes?
For example if one uploaded a nuget package that was a plugin, and it contained its own EDM model describing its Models and Controllers...all this happening way after after.Run() was invoked, how can one kick the system in the head to refind/relearn what are valid routes?
Q6: Why is $count only available on ODataControllers that are registered by Convention, but doesn't work on those registered by RouteAttribute?
Background
The latest(?) documentation I found in a blog post here:
https://devblogs.microsoft.com/odata/attribute-routing-in-asp-net-core-odata-8-0-rc/
But it's just a blog post, and a lot remains missing to understand all the pieces and how they fit together.
Process So Far
I'm listing my notes below to help others see better what I am trying, in case someone can see what I am obviously getting wrong.
And if it helps and you want working code, I've uploaded my investigation efforts (actually thrashingArounds):
https://github.com/skysgh/Spikes.AspNetCore.ODataRouting
Setup
The default scenario in OData is to register EDM models with Controller Routes equal to the prefix of the name of the Controller.
Eg:
static string ODataPrefixWithSlash = "api/odata/v{version}/"
class SomeModelController : ODataController {...}
//is registered in an EDM model using convention of matching prefix of controller name:
var builder = new ODataConventionModelBuilder();
//to build a whole model:
builder.EntitySet<SomeModel>("SomeModel"); //This will get found
builder.EntitySet<SomeModel>("Renamed"); //Without further work causes 404 as route string != controller prefix
var edmModelA = builder.Build();
//and the model later registered as the source of OData routing info:
var mvcBuilder = builder.Services
.AddControllers()
.AddOData(
opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5)
//Add Module/PluginA Routes:
.AddRouteComponents(AppAPIConstants.ODataPrefixWithSlash,edmModelA);
if one has enabled
builder.Services.AddSwaggerGen();
app.UseODataRouteDebug();
one can navigate to ~odata and see the controller's Endpoints listed as:
~api/odata/v{version}/SomeModel
~api/odata/v{version}/SomeModel/$count <- note, showing for now (will break later...why?!)
Controller Choice Process
An article exists that talks about how routing has been updated in OData v8 RC, relying on RouteAttribute
https://devblogs.microsoft.com/odata/attribute-routing-in-asp-net-core-odata-8-0-rc/
After a lot of messing around, appears one can use a different name but
only IF one starts the route with the same prefix used to register the EDM Model (eg: api/odata/v{version}/
[ODataAttributeRouting]
// Half Works
// because the route starts with the same base as what the model was registered under
// fools it(?) (where?) into accepting it as an Odata controller.
// a) listed in ~/$odata as an odata controller (under api/odata/v{version}
// b) acting as an Odata controller (returning odata wrapper in json)
// c) but no default queryability (doesn't accept or list /$count)
// as the route starts with same prefix
// as Convention used when registereing EDM model
// But $count doesn't work!
[Route(AppAPIConstants.ODataPrefixWithSlash + "Renamed5")]
public class ValuesA5Controller : ODataController{
[EnableQuery(PageSize = 100)]
[HttpGet("")]
[HttpGet("Get")]
public IActionResult Get()
{
return Ok(FakeDataBuilder.Get());
}
}
The above permits registering the ODataController in the EDM model as follows:
builder.EntitySet<SomeModel>("Renamed5");
But it only HALF works.
it is listed as an OData Controller in $odata. ok.
it is acting (mostly) as an ODataController in that it accepts most OData commands $select, $filter, etc.
But it is failing at offering $count for some reason:
~api/odata/v{version}/Renamed5 <- showing, same as it was doing for earlier SomeModelController
~api/odata/v{version}/Renamed5/$count <- not showing, even though endpoint decorated with [EnableQuery].
Options?
Option A: More Route information
I suspect that one could decorate the Get method with more routes to enable $count:
[EnableQuery(PageSize = 100)]
[HttpGet("")]
[HttpGet("Get")]
[HttpGet("$count") ??? <- really???
public IActionResult Get() {....}
Certainly wish to avoid adding more codes as workaround (eg: what was tried here: https://stackoverflow.com/a/73042175/19926885)
but even if either worked, its more code, more sources of errors, etc. to watch out for.
If at all possible I'd like the least code, the most convention, while not locking in controller names.
Option B: Controller Selection process
As I said earlier, I don't know how or where the odata framework is magically matching "SomeModel" to "SomeModelController".
In an article I came across (https://devblogs.microsoft.com/odata/attribute-routing-in-asp-net-core-odata-8-0-rc/)
I saw mention of AttributeRoutingConvention: IODataControllerActionConvention which maybe could be put to use, but the blog post
didn't show when/how it could be registered, or replaced so have not been able to progress in that direction yet.
Also, iterating through registered Services I don't see anything inheriting from IODataControllerActionConvention. What's going on?
What I do see is:
//not sure yet what these do
IODataQueryRequestParser
IODataTemplateTranslator : Microsoft.AspNetCore.OData.Routing.Template.DefaultODataTemplateTranslator
IODataPathTemplateParser : Microsoft.AspNetCore.OData.Routing.Parser.DefaultODataPathTemplateParser`
But I don't have documentation on how they work, what they are for, etc.
Where next?
First of all THANK YOU for taking the time to read this long question!
Second, if you have advice as how I not thinking right as to how to solve the problem, that would help.
Third, if you are able to provide answers to the questions...wow. It's been a while I've looked for answers to this!
Took me a while to recognise the following basic misunderstandings on my part.
OData routing not something else but is an instance of the underlying WebAPI Routing
When one registers an EDM and providing the route prefix ("api/odata/v{version}"), it's just registering "~/api/odata/v{version}/~" as an action/route filter.
If you don't provide a [RouteAttribute] the convention is that it will make it up the Route segment from the Controller name (eg: 'PersonController' means that 'Person' will be used.
If you do provide a [RouteAttribute] on the Controller, you are saying this is the full (not a suffix!) route -- even if you provided a prefix when registering the EDM model.... To underline that point: just because HttpGet(...) acts as a suffix to the Route, doesn't meant the Route does too.
Which means that if it doesn't start with [Route("api/odata/v{version}")] -- maybe is just [Route("Renamed6")] -- it **doesn't match the route prefix used to register the edm model, so won't be handled by the models handler...so all it's doing is acting like all WebAPI controllers are doing and registers another route (whatever you gave), but won't consider it worthy of being OData queryability enabled....
So your route has to be [Route("api/odata/v{version}/Renamed6")].
Other than that, basics are that you'll get it confused if you register two models one with
"api/odata/v{version}", and another with
"api/odata/v{version}/plugin".
The first handler/whatever (i don't know what's behind the scenes here) will probably try to capture requests to plugin and not find a controller called "PluginController". I'm just guessing.
Finally, I think I saw issues with leaving dynamics tokens at the end for some reason. I had to change from using api/odata/PluginA/v{version} to api/odata/v{version}/PluginA . Not clear why.
Admittedly I still need to know a LOT more about the routing mechanism, but for now, this gets me forward again.
PS: Also...use Constants for building route strings. Turns out that Typos can really waste a lot of time :-(

What is the difference between AddAuthentication and AddAuthenticationCore?

Looking at the code for AuthenticationServiceCollectionExtensions.AddAuthentication() vs AuthenticationCoreServiceCollectionExtensions.AddAuthenticationCore(), it looks like AddAuthentication implicitly calls AddAuthenticationCore, adds some other goodies, and then returns a new instance of AuthenticationBuilder instead of just returning IServiceCollection.
Am I understanding the code correctly? If so, are there generally any reasons to call AddAuthenticationCore instead of AddAuthentication outside of writing your own extension?
It seems to be a typical pattern in ASP.NET Core: the Add[xxx]Core methods add the bare minimum to enable a feature, but without any bells and whistles. It's also probably used to make unit testing the core features easier.
You can make a parallel with the AddMvc vs AddMvcCore methods. There's a question that asks Should I use AddMvc or AddMvcCore for ASP.NET Core MVC development?, and the gist is that it allows for fine-grained control on what middleware you want to use.
To answer your question: for a typical user, there's probably no reason to use AddAuthenticationCore.
Actually there is a reason. Currently AddAuthentication() also adds data protection services, which you may not need - for example if you are writing your own Authentication Scheme. So instead you can do this:
services.AddAuthenticationCore(o => {
o.DefaultScheme = "My Custom Scheme";
});
services.AddWebEncoders();
services.AddSingleton<ISystemClock, SystemClock>();
var authBuilder = new AuthenticationBuilder(services);
however I fully expect this to break in future versions of asp.net core as it's undocumented and a bit of a hack.

Autofac Multitenant Database Configuration

I have a base abstract context which has a couple hundred shared objects, and then 2 "implementation" contexts which both inherit from the base and are designed to be used by different tenants in a .net core application. A tenant object is injected into the constructor for OnConfiguring to pick up which connection string to use.
public abstract class BaseContext : DbContext
{
protected readonly AppTenant Tenant;
protected BaseContext (AppTenant tenant)
{
Tenant = tenant;
}
}
public TenantOneContext : BaseContext
{
public TenantOneContext(AppTenant tenant)
: base(tenant)
{
}
}
In startup.cs, I register the DbContexts like this:
services.AddDbContext<TenantOneContext>();
services.AddDbContext<TenantTwoContext>();
Then using the autofac container and th Multitenant package, I register tenant specific contexts like this:
IContainer container = builder.Build();
MultitenantContainer mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container);
mtc.ConfigureTenant("1", config =>
{
config.RegisterType<TenantOneContext>().AsSelf().As<BaseContext>();
});
mtc.ConfigureTenant("2", config =>
{
config.RegisterType<TenantTwoContext>().AsSelf().As<BaseContext>();
});
Startup.ApplicationContainer = mtc;
return new AutofacServiceProvider(mtc);
My service layers are designed around the BaseContext being injected for reuse where possible, and then services which require specific functionality use the TenantContexts.
public BusinessService
{
private readonly BaseContext _baseContext;
public BusinessService(BaseContext context)
{
_baseContext = context;
}
}
In the above service at runtime, I get an exception "No constructors on type 'BaseContext' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'". I'm not sure why this is broken....the AppTenant is definitely created as I can inject it other places successfully. I can make it work if I add an extra registration:
builder.RegisterType<TenantOneContext>().AsSelf().As<BaseContext>();
I don't understand why the above registration is required for the tenant container registrations to work. This seems broken to me; in structuremap (Saaskit) I was able to do this without adding an extra registration, and I assumed using the built in AddDbContext registrations would take care of creating a default registration for the containers to overwrite. Am I missing something here or is this possibly a bug in the multitenat functionality of autofac?
UPDATE:
Here is fully runable repo of the question: https://github.com/danjohnso/testapp
Why is line 66 of Startup.cs needed if I have lines 53/54 and lines 82-90?
As I expected your problem has nothing to do with multitenancy as such. You've implemented it almost entirely correctly, and you're right, you do not need that additional registration, and, btw, these two (below) too because you register them in tenant's scopes a bit later:
services.AddDbContext<TenantOneContext>();
services.AddDbContext<TenantTwoContext>();
So, you've made only one very small but very important mistake in TenantIdentitifcationStrategy implementation. Let's walk through how you create container - this is mainly for other people who may run into this problem as well. I'll mention only relevant parts.
First, TenantIdentitifcationStrategy gets registered in a container along with other stuff. Since there's no explicit specification of lifetime scope it is registered as InstancePerDependency() by default - but that does not really matter as you'll see. Next, "standard" IContainer gets created by autofac's buider.Build(). Next step in this process is to create MultitenantContainer, which takes an instance of ITenantIdentitifcationStrategy. This means that MultitenantContainer and its captive dependency - ITenantIdentitifcationStrategy - will be singletons regardless of how ITenantIdentitifcationStrategy is registered in container. In your case it gets resolved from that standard "root" container in order to manage its dependencies - well, this is what autofac is for anyways. Everything is fine with this approach in general, but this is where your problem actually begins. When autofac resolves this instance it does exactly what it is expected to do - injects all the dependencies into TenantIdentitifcationStrategy's constructor including IHttpContextAccessor. So, right there in the constructor you grab an instance of IHttpContext from that context accessor and store it for using in tenant resolution process - and this is a fatal mistake: there's no http request at this time, and since TenantIdentitifcationStrategy is a singleton it means that there will not ever be one for it! So, it gets null request context for the whole application lifespan. This effectively means that TenantIdentitifcationStrategy will not be able to resolve tenant identifier based on http requests - because it does not actually analyze them. Consequently, MultitenantContainer will not be able to resolve any tenant-specific services.
Now when the problem is clear, its solution is obvious and trivial - just move fetching of request context context = _httpContextAccessor.HttpContext to TryIdentifyTenant() method. It gets called in the proper context and will be able to access request context and analyze it.
PS. This digging has been highly educational for me since I had absolutely no idea about autofac's multi-tenant concept, so thank you very much for such an interesting question! :)
PPS. And one more thing: this question is just a perfect example of how important well prepared example is. You provided very good example. Without it no one would be able to figure out what the problem is since the most important part of it was not presented in the question - and sometimes you just don't know where this part actually is...

ServiceStack and NHibernate Unit Of Work Pattern

Long story as brief as possible...
I have an existing application that I'm trying to get ServiceStack into to create our new API. This app is currently an MVC3 app and uses the UnitOfWork pattern using Attribute Injection on MVC routes to create/finalize a transaction where the attribute is applied.
Trying to accomplish something similar using ServiceStack
This gist
shows the relevant ServiceStack configuration settings. What I am curious about is the global request/response filters -- these will create a new unit of work for each request and close it before sending the response to the client (there is a check in there so if an error occurs writing to the db, we return an appropriate response to the client, and not a false "success" message)
My questions are:
Is this a good idea or not, or is there a better way to do
this with ServiceStack.
In the MVC site we only create a new unit
of work on an action that will add/update/delete data - should we do
something similar here or is it fine to create a transaction only to retrieve data?
As mentioned in ServiceStack's IOC wiki the Funq IOC registers dependencies as a singleton by default. So to register it with RequestScope you need to specify it as done here:
container.RegisterAutoWiredAs<NHibernateUnitOfWork, IUnitOfWork()
.ReusedWithin(ReuseScope.Request);
Although this is not likely what you want as it registers as a singleton, i.e. the same instance returned for every request:
container.Register<ISession>((c) => {
var uow = (INHibernateUnitOfWork) c.Resolve<IUnitOfWork>();
return uow.Session;
});
You probably want to make this:
.ReusedWithin(ReuseScope.Request); //per request
.ReusedWithin(ReuseScope.None); //Executed each time its injected
Using a RequestScope also works for Global Request/Response filters which will get the same instance as used in the Service.
1) Whether you are using ServiceStack, MVC, WCF, Nancy, or any other web framework, the most common method to use is the session-per-request pattern. In web terms, this means creating a new unit of work in the beginning of the request and disposing of the unit of work at the end of the request. Almost all web frameworks have hooks for these events.
Resources:
https://stackoverflow.com/a/13206256/670028
https://stackoverflow.com/search?q=servicestack+session+per+request
2) You should always interact with NHibernate within a transaction.
Please see any of the following for an explanation of why:
http://ayende.com/blog/3775/nh-prof-alerts-use-of-implicit-transactions-is-discouraged
http://www.hibernatingrhinos.com/products/nhprof/learn/alert/DoNotUseImplicitTransactions
Note that when switching to using transactions with reads, be sure to make yourself aware of NULL behavior: http://www.zvolkov.com/clog/2009/07/09/why-nhibernate-updates-db-on-commit-of-read-only-transaction/#comments

Ninject dependency into AuthorizationAttribute MVC4 Web API RC

I have a custom authorization attribute, required only for some actions, which checks the request headers for a custom token. The token is checked in a database. Checking the database requires access to a service which I would like to have injected through the constructor.
The way I have read this can be done (here, here, and here) is by having a constructor-less filter and injecting the dependent one like this:
kernel.BindFilter<MyAuthorizeFilter>(FilterScope.Controller, 0).WhenControllerHas<MyAuthorizeAttribute>();
However the BindFilter method is not available to me as I have setup Ninject as described here. This is using Ninject.Web.Common instead of Ninject MVC3 as I read that Ninject MVC3 would not work with MVC4 RC. How else can I go about accomplishing this?
I have read also that I could add to GlobalFilters.Filters - however I don't want it to be present on every action.
Thanks in advance for any answers.
I'm not completely sure I see how you have set up your application, but my experience has been that if you want a filter for a WebApi controller you need to add it to the HttpFilterCollection that is available from the GlobalConfiguration.Filters. This is a Different set of filters than what MVC uses (through the GlobalFilterCollection).