ODataClient MaxProtocolVersion V3 - c#

I am trying to consume OData from a windows forms. So, what i have done to now is create a new project, i added a web service reference to the OData service and try to consume it.
My code is:
var VistaEntities = new VrExternalEntities("serviceURI");
var query = VistaEntities.VRtblCinemaType
.Where(
x =>
x.VRtblCinema_Operators
.Any
(
z =>
z.VRtblSessions
.Any
(
y =>
y.Session_dtmDate_Time > DateTime.Now
)
)
)
.Select
(
x =>
new
{
x.CinType_strCode,
x.CinType_strDescription
}
);
If i remove the Where clause it works. If i do it says that Any is not supported. I know i have to set MaxProtocolVersion to V3 but i do not know how to do it. I don't have an entity context or anything else. I only have what i have stated above.
Please provide steps on how to accomplish that.
Thanks in advance.
Giannis

You must retrieve the configuration of your DataService and set the MaxProtocolVersion of its behavior to V3.
The best place to do this is certainly in the InitializeService static method you can define in your service class, which will be given the proper configuration object as its config parameter by the environment. It will only be invoked once, typically at the first request.
Note #1: You need WCF Data Services 5.0 or greater. The best way to get it is probably via the Server NuGet package.
Note #2: Oddly enough, the DataServiceProtocolVersion type, although in the Common namespace, is included in the Client assembly (Microsoft.Data.Services.Client, provided by the Client NuGet package). I suggested a better organization here.
public class Vista : DataService<VistaContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule(...);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
...
}
}
Update:
The client may indeed specify the desired version in the requests by using the DataServiceVersion HTTP header. It's currently recommended that you specify and support a range of versions using the MinDataServiceVersion and MaxDataServiceVersion headers if you can, for obvious reasons. Note however that the MinDataServiceVersion will be removed in OData 4.0 (see appendix E.1 of part 1 and "What's new" documents drafts).
The relevant documentation for the WCF Data Services 5.x implementation is available here. The documentation specific to the client seems pretty scarce, but looking at the reference you can see that you must use this constructor for the DataServiceContext to specify the maximum protocol version, and it looks like you cannot change it at any one point for subsequent requests without rebuilding a new context. You may attempt to fiddle with the headers directly, but I wouldn't expect it to work reliably (or at all).
So, to answer your question, you really need control over how you create the context for the client.

Related

How to dynamically change OpenID configuration based on something in a single ASP.NET Core application?

We have some OpenID configuration specified in ConfigureServices in Startup.cs:
services.AddOpenIdConnect("something", "Something", options =>
{
// ... //
});
How can we change the configuration we've outlined here dynamically, on a per request basis, based on certain rules?
Can this be done in a middleware? If so, please give an example, thank you!
Don't think you can do that, but you can if you want add multiple services.AddOpenIdConnect(...) handlers and use a different one for different clients.
What kind of usecase do you have? What do you try to create?
There's nothing stopping you from adding the source of the OpenIdConnectHandler to your own application and then tweaking it to your needs. Its pretty simple and I have done that myself to learn the inner workings of it.
The source is here:
https://github.com/dotnet/aspnetcore/tree/master/src/Security/Authentication/OpenIdConnect
if (await _authenticationSchemeProvider.GetSchemeAsync(OpenIdConnectDefaults.AuthenticationScheme) == null)
_authenticationSchemeProvider.AddScheme(new AuthenticationScheme(OpenIdConnectDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme, typeof(OpenIdConnectHandler)));
var opendIDoption = new OpenIdConnectOptions();
OpenIDSSOConfiguration.SetupOIDOption(opendIDoption, openIDSSOConfiguration);
foreach(var postConfigure in _postConfigures)
{
postConfigure.PostConfigure(OpenIdConnectDefaults.AuthenticationScheme, opendIDoption);
}
_optionsCache.TryRemove(OpenIdConnectDefaults.AuthenticationScheme);
_optionsCache.TryAdd(OpenIdConnectDefaults.AuthenticationScheme, opendIDoption);
_postConfigureOptions.PostConfigure(OpenIdConnectDefaults.AuthenticationScheme, opendIDoption);
Here is my working solution of my.
serwices used are:
IEnumerable<IPostConfigureOptions<OpenIdConnectOptions>> postConfigures,
IPostConfigureOptions<OpenIdConnectOptions> postConfigureOptions,
IOptionsMonitorCache<OpenIdConnectOptions> optionsCache,
IAuthenticationSchemeProvider authenticationSchemeProvider)
Applying postConfigureOptions is crucial because OpenIdConnectHandler will throw exception that OpenIdConnectOptions is not correct configured.

ASP.NET and C#: where do you store the rest client url?

I have my rest client url hard-coded in my code-behind, but upon peer-reviewing my code, I was asked to move that url to the config file so that it can be changed for each environment.
Visual Studio 2019 now complains because my rest client url has an invalid = sign as token in the url itself, and it expects ; as token instead.
Has anyone ever come across this, and is it correct to move the rest client to the config file? In theory that should not change.
Can't share the full url, but the part that is highlighted as error is this: version=2.0&details=true.
I found the answer. The problem is in the & symbol itself. Once converted to &, the errors Visual Studio was highlighting were gone and my solution worked again.
If i will do that i will save in config file only base url like this
"WebConfig": {
"SmsCenterApi": "https://some_site.com/SendService"
}
and in code I can complete the link
string url = WebConficData.SmsCenterApi+"version=2.0&details=true";
andafter that I can use url to make some request. For multi-environments web.config and appsettings is avesome. You just change base url for each env and that's it.
I think the answer to your questions
where do you store the rest client url?
is it correct to move the rest url to the config file?
is dependent on how you implement the rest request to that url. As you do not show any information on how you implement the rest call, I would like to show you one possible way and hopefully give you an impression about which things you should consider when implementing a solution.
So we can basically (for the sake of completeness) split an rest-endpoint url into two parts which might affect our implementation.
The base url:
"https://www.some-nice-name.com/SomeEndPoint"
and the parameters
?key1=value1&key2=value2
having this in mind, you could go the way and split them up, storing the base url and the parameters in two different nodes/attributes in a config file:
{
"BaseUrl" : "https://www.some-nice-name.com/SomeEndPoint",
"UrlParams" : "?key1=value1&key2=value2"
}
Or in one node/attribute, or even split each single parameter pair ("key1=value1") into own fields. And so on, and so on......
Anyway, if we now jump into our C# code and implement the Rest call, we have a wide range of different possible solution. For this example I will show you how to use the RestSharp NuGet package and why it might influences our decision on the above question.
So one basic example:
// I will not show the implementation of ReadYourConfigStuff() because its fairly clear what should happen here
var config = ReadYourConfigStuff();
// Give the BaseUrl to our REST client
var restClient = new RestClient(config.BaseUrl);
// Just taking GET as we have some GET Parameters
var restRequest = new RestRequest(Method.GET);
so far so good. But we still miss our parameters right?
Let's go ahead:
// RestSharp gives us a very nice tool to add GET parameters to our request
restRequest.AddParameter("key1", "value1");
restRequest.AddParameter("key2", "value2");
That looks quite different to what we added to our config file, does it? Yes it does. As RestSharp gives us a tool at hand which allows to add parameters one by one, we are free to choose how to store and maintain those in our code. And if we have a look on the AddParameter defintion
public IRestRequest AddParameter(string name, object value);
we see that the second parameter can be any object. Amazing!
So my answer to your question is: Yes you can store it in the config file, but does it fit to your implementation? Are the parameters fix or do they change? How does your favorite tooling would like you to implement the rest request?
Based on the answers to these questions, I would take a decision rather to use a config file or not.

Is there a way to use the 'AllowAnyOrigin' property on a CorsPolicy within .NET Core?

I'm currently working on integrated Cors within a .Net Core app. I have previously dealt with Cors within the full .NET Framework (4.6.1) which had the ability to set the AllowAnyOrigin property within a given CorsPolicy.
Like mentioned before, I have written a .NET Core WebApi, which I am trying to implement Cors into. I have it reading from an AppSetting stored in my appSettings.json, from which it can be either three things:
If the appsetting is set to an asterisk, then I would like to see the AllowAnyOrigin property like I have done in the full .NET framework. (This is where my problem lies.)
If the appsetting is set to a comma seperated string, such as https://example.com, https://test.com, then this is applied onto the policy as needed.
If the appsetting has been commented out or left as empty, then I am reading a list of rows from Azure Table Storage to supply a list of given origins.
Below is an example of the full .NET framework that I have dealt with before.
var origins = ConfigurationManager.AppSettings[KeyCorsAllowOrigin];
switch (origins)
{
case null:
_corsPolicy.Origins.Clear();
foreach (var item in new StorageConfigurationManager().GetRowKeys())
{
_corsPolicy.Origins.Add("https://" + item);
}
break;
case "*":
_corsPolicy.AllowAnyOrigin = true;
break;
default:
_corsPolicy.AllowAnyOrigin = false;
if (!string.IsNullOrWhiteSpace(origins)) AddCommaSeparatedValuesToCollection(origins, _corsPolicy.Origins);
break;
}
I thought that I could replicate this functionality within .NET Core and the Microsoft.AspNetCore.Cors.Infrastructure package, but it seems that Microsoft has restricted access to setting the property, and can only be read from.
Does anyone know of any way to set this?
I know you can build the CorsPolicy within the pipeline, which then uses the .AllowAnyOrigin(), but I am currently using custom Cors middleware to help with my custom policy.
There is a great article called Enabling CORS in ASP.NET Core I'll sum the interesting part for you:
to only allow GET methods on your resource, you can use the WithMethods method when you define the CORS policy:
services.AddCors(options =>
{
options.AddPolicy("AllowOrigin",
builder => builder.WithOrigins("http://localhost:55294")
.WithMethods("GET"));
});
If you need to allow any origin to access the resource, you will use AllowAnyOrigin instead of WithOrigins:
services.AddCors(options =>
{
options.AddPolicy("AllowOrigin",
builder => builder.AllowAnyOrigin());
});
Just as an answer to all this, and so that perhaps someone can be helped by this question. I looked at the source code for the AllowAnyOrigin method within the CorsPolicyBuilder class and saw how this was handled. I nearly had the solution, just forgot to clear the Origins list beforehand.
_policy.Origins.Clear();
_policy.Origins.Add(CorsConstants.AnyOrigin);

How can I make url path in Swashbuckle/Swaggerwork when api is served from inside another project?

all. I am trying to document a WebApi 2 using Swashbuckle package.
All works great if the API is running by itself i.e. localhost/api/swagger brings me to ui and localhost/api/swagger/docs/v1 to json.
However the producation app initializes this same Webapi project by running webapiconfig method of this project from global.asax.cs in another - now web project (the main application one). So the api url looks like localhost/web/api instead of localhost/api.
Now swashbuckle doesn't work like that at all.
localhost/api/swagger generates error cannot load
'API.WebApiApplication', well of course
localhost/web/swagger = 404
localhost/web/api/swagger = 404
I tried to look everywhere, but all I found is workaround.
c.RootUrl(req => req.RequestUri.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/").TrimEnd('/'));
Unfortunately it doesn't work, now maybe it should and I just need to change something but I don't even know what exactly this property expects and what it should be set to.
May be it's not even applicable - maybe setup we have requires something else or some swashbuckle code changes.
I will appreciate any help you can provide. I really starting to like swagger (and swashbuckle) for rest documentation.
For Swashbuckle 5.x:
This appears to be set by an extension method of httpConfiguration called EnableSwagger. Swashbuckle 5.x migration readme notes that this replaces SwaggerSpecConfig. SwaggerDocConfig RootUrl() specifically replaces ResolveBasePathUsing() from 4.x.
This practically works the same as it did before, looks like the biggest change was that it was renamed and moved into SwaggerDocConfig:
public void RootUrl(Func<HttpRequestMessage, string> rootUrlResolver)
An example from the readme, tweaked for brevity:
string myCustomBasePath = #"http://mycustombasepath.com";
httpConfiguration
.EnableSwagger(c =>
{
c.RootUrl(req => myCustomBasePath);
// The rest of your additional metadata goes here
});
For Swashbuckle 4.x:
Use SwaggerSpecConfig ResolveBasePathUsing and have your lambda read your known endpoint.
ResolveBasePathUsing:
public SwaggerSpecConfig ResolveBasePathUsing(Func<HttpRequestMessage, string> basePathResolver);
My API is behind a load balancer and this was a helpful workaround to providing a base address. Here's a dumb example to use ResolveBasePathUsing to resolve the path with a known base path.
string myCustomBasePath = #"http://mycustombasepath.com";
SwaggerSpecConfig.Customize(c =>
{
c.ResolveBasePathUsing((req) => myCustomBasePath);
}
I hardcoded the endpoint for clarity, but you can define it anywhere. You can even use the request object to attempt to cleanup your request uri to point to /web/api instead of /api.
The developer commented on this workaround on GitHub last year:
The lambda takes the current HttpRequest (i.e. the request for a given
Swagger ApiDeclaration) and should return a string to be used as the
baseUrl for your Api. For load-balanced apps, this should return the load-balancer path.
The default implementation is as follows:
(req) => req.RequestUri.GetLeftPart(UriPartial.Authority) + req.GetConfiguration().VirtualPathRoot.TrimEnd('/');
...
Re relative paths, the Swagger spec requires absolute paths because
the URL at which the Swagger is being served need not be the URL of
the actual API.
...
The lambda is passed a HttpRequestMessage instance ... you should be able to use this to get at the RequestUri etc. Another option, you could just place the host name in your web.config and have the lambda just read it from there.

How can you programatically add mappings to UnicastBusConfig?

I would like to be able to add subscriptions to additional message types living on potentially different servers to my application at runtime. What I'm trying to achieve is a Monitoring application where I can add/remove subscriptions at runtime. Is this possible? How do I get a reference to the current UnicastBus mappings?
Here is what I'm doing so far, but I believe this will overwrite any mappings currently in existence.
MessageEndpointMappingCollection mappings = new MessageEndpointMappingCollection();
mappings.Add(new MessageEndpointMapping()
{
Messages = m.MessageType.ToString(),
Endpoint = m.QueueName
});
IComponentConfig<UnicastBusConfig> busConfig = config.Configurer.ConfigureComponent<UnicastBusConfig>(ComponentCallModelEnum.None);
busConfig.ConfigureProperty(u => u.MessageEndpointMappings, mappings);
Yves used this code in his Azure samples (to be found in NSB samples collection)
using NServiceBus.Config;
using NServiceBus.Config.ConfigurationSource;
namespace OrderService
{
class ConfigOverride : IProvideConfiguration<UnicastBusConfig>
{
public UnicastBusConfig GetConfiguration()
{
return new UnicastBusConfig
{
MessageEndpointMappings = new MessageEndpointMappingCollection
{
new MessageEndpointMapping { Messages="MyMessages", Endpoint="orderserviceinputqueue" }
}
};
}
}
}
The best way to approach this would be to implement IConfigurationSource and provide your own configuration. Then you could cherry pick what you would like to load from the config file (if anything) and what you would like to specify yourself at runtime.
I would reflect the DefaultConfigurationSource class or refer to this gist for guidance.
In a project, I am currently involved with, we are doing some content-based routing to dynamically subscribed/unsubscribed agents by keeping track of them in our own routing table.
We have wrapped the IBus in a decorator (by using Windsor's support of decorators, as described <plug>here</plug>), that sees if the message implements a special IRoutableMessage interface, that allows the decorator to route the message by explicitly specifying the destination endpoint via bus.Send(destinationEndpoint).
This was a little bit complex to get right, and I would recommend going with NServiceBus' built-in routing as far as possible. But is is possible to explicitly route messages to any endpoint.
If you are looking into monitoring, check out the NSBManager repository. This takes the opposite approach and lets the endpoints register with the manager.

Categories

Resources