.Net6 Upgrade with OData v8, Broken DI - c#

I have a WEB API running on.Net5 for a while with the OData package.
Recently I've started to undertake the upgrade for .Net6 work but OData compatibility is broken at first. I needed to upgrade the OData package as well from v7 to v8.
After the upgrade first, my package references were broken so I've changed them from;
"using Microsoft.AspNet.OData;"
to
"using Microsoft.AspNetCore.OData;"
Now that resolved many of the errors, but coming back to my OData Config, my
services.AddOData();
has started to throw the error of "IServiceCollection' does not contain a definition for 'AddOData' and the best extension method overload 'ODataMvcBuilderExtensions.AddOData(IMvcBuilder)' requires a receiver of type 'IMvcBuilder'"
After some research, I've changed that to AddControllers first "services.AddControllers().AddOData();" and now my config file is like this;
public static void SetupOData(this IServiceCollection services)
{
// OData Support
//services.AddOData();
services.AddControllers().AddOData();
// In order to make swagger work with OData
services.AddMvcCore(options =>
{
foreach (OutputFormatter outputFormatter in options.OutputFormatters.OfType<OutputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
{
outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
foreach (InputFormatter inputFormatter in options.InputFormatters.OfType<InputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
{
inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
});
}
However after adding Config to the Startup with
// OData
services.SetupOData();
and
app.UseEndpoints(endpointRouteBuilder =>
{
endpointRouteBuilder.MapControllers();
// OData configuration
endpointRouteBuilder.EnableDependencyInjection();
endpointRouteBuilder.Filter().Select().Count().OrderBy();
});
I am getting error on EnableDependencyInjection() "'IEndpointRouteBuilder' does not contain a definition for 'EnableDependencyInjection' and no accessible extension method 'EnableDependencyInjection' accepting a first argument of type 'IEndpointRouteBuilder' could be found (are you missing a using directive or an assembly reference?)
So my OData usage is taking the non-EDM route and tried to implement that as simple as possible. But now after the upgrade, I am completely confused and or blinded right now. Can you help me get through this?

So an update;
Changing the OData config worked for me;
Instead of adding Odata first;
services.AddOData();
I pulled it inside the MvcCore;
services.AddMvcCore(options =>
{
foreach (OutputFormatter outputFormat ......."odatatestxx-odata"));
}
}).AddOData();
And then instead of using DI on endpointRouteBuilder, just kept MapControllers as;
endpointRouteBuilder.MapControllers();

Related

Use AddServerSideBlazor() from inside a class library

I am trying to setup my own Service Collection like so:
public static void AddMyServices(this IServiceCollection services)
{
services.AddServerSideBlazor();
//services.AddTransient<IMyService, MyService>();
}
I can't figure out how to get this running, I tried adding several NuGet References but none does work. e.g. Microsoft.AspNetCore.Components.Web to get AddServerSideBlazor(); working.
I want to use it like this in my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMyServices();
}
The Error I get:
'IServiceCollection' does not contain a definition for
'AddServerSideBlazor' and no accessible extension method
'AddServerSideBlazor' accepting a first argument of type
'IServiceCollection' could be found (are you missing a using directive
or an assembly reference?)
Edit:
After some more looking into it I think the correct package is
Microsoft.AspNetCore.App.Ref but adding it via NuGet gives me this error:
The package Microsoft.AspNetCore.App.Ref 5.0.0 has a package type
DotnetPlatform that is incompatible with this project.
You will need to Extend the IServiceCollection.
public static Microsoft.Extensions.DependencyInjection.IServerSideBlazorBuilder AddServerSideBlazor (this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Action<Microsoft.AspNetCore.Components.Server.CircuitOptions>? configure = default);
found https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.componentservicecollectionextensions.addserversideblazor?view=aspnetcore-5.0

Swagger fails when using Microsoft.AspNetCore.OData v8.0.0-preview2

I am working on an ASP.NET Core 5 API project and I am using Microsoft.AspNetCore.OData v8.0.0-preview2.
If I use the out-of-box ASP Net Web application template, before I add OData, Swagger works just fine.
However, as soon as I add the following code;
services.AddOData(
option => option.AddModel("v1", GetV1EdmModel())
.Select()
.Expand()
.Filter()
.OrderBy()
.Count());
... to my Startup.cs --> ConfigureServices method and then try to navigate to the ../swagger endpoint the swagger page loads but displays the following error;
Failed to load API definition
Fetch error
unidentified /swapper/v1/swagger.json
Is swagger not yet supported in the Microsoft.AspNetCore.OData v8.0.0-preview2 release? Or am I missing something.
I have read through Sam Xu's blog posts; ASP.NET Core OData 8.0 Preview for .NET 5 and Routing in ASP.NET Core OData 8.0 Preview
But there was no mention of this issue.
Does anyone have a solution or a work around?
You can find the error message in the response:
System.InvalidOperationException: No media types found in 'Microsoft.AspNetCore.OData.Formatter.ODataOutputFormatter.SupportedMediaTypes'. Add at least one media type to the list of supported media types.
Try add below code after services.AddOData(...).
services.AddMvcCore(options =>
{
foreach (var outputFormatter in options.OutputFormatters.OfType<ODataOutputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
foreach (var inputFormatter in options.InputFormatters.OfType<ODataInputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
});
And the same issue on github.

How to load controllers from different versions of the same assembly

I am trying to solve the following problem and i am not exactly sure how to do it:
I am building a web server that has differenty APIs/Controllers that are loaded from .dll-Files on Startup. It will run in a linux docker container and is implemented as an ASP-NET Webapplication & .net Core 2.1 .
The loading of assemblies that contain controllers works fine by doing something like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMvc().AddApplicationPart(AssemblyLoadContext.Default.LoadFromAssemblyPath("/PATH/APIPlugin.dll"));
}
This application must have versioned REST-APIs that means: I need to load the same assembly multiple times in different versions. then i need to have some kind of routing between the versions.
For example:
/api/data/latest/
/api/data/v1/
I cannot use AssemblyLoadContext.Default.LoadFromAssemblyPath to load multiple versions of the same assembly. I also tried to grab the controller from the assembly and creating an instance of it like this:
var controllerAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath("/PATH/APIPlugin.dll");
var pluginType = controllerAssembly ExportedTypes.First<Type>();
var pluginInstance = (ControllerBase)Activator.CreateInstance(pluginType);
services.Add(new ServiceDescriptor(pluginType, pluginInstance));
This throws no exception but ultimately does not work. But i am pretty new to ASP.Net so this might very well be nonsense and i would have to find a solutuion to route between the different versions, even if it would work like this.
My Question:
How would one approach this requirement ?
Is it a god idea/possible to load multiple Controllers from the "same" assembly ? If yes, how would one achieve this?
Or would it be a better solution to have one controller that does all the routuing and load some self-defined implementation from the assemblies. So that the controller would route between the versions, and api-methods?
I was able to find a sultion while tinkering around:
public class ControllerPluginProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var basePath = AppContext.BaseDirectory;
var pluginPath = Path.Combine(basePath, "plugins");
foreach (var file in Directory.GetFiles(pluginPath, "*.dll")){
var assembly = Assembly.LoadFile(file);
var controllers = assembly.GetExportedTypes().Where(t => typeof(ControllerBase).IsAssignableFrom(t));
foreach (var candidate in controllers)
{
feature.Controllers.Add(candidate.GetTypeInfo());
}
}
}
}
In Startup:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc().ConfigureApplicationPartManager(m =>
m.FeatureProviders.Add(new ControllerPluginProvider()));
}
This lead to the following error when the same assembly, and therefore a Controller with the same name was loaded: Attribute routes with the same name 'Get' must have the same template
I was able to fix it, and also add versioning with the versioning library:
https://github.com/microsoft/aspnet-api-versioning
[ApiVersion("2.0")]
[ApiController]
[Route("api/v{version:apiVersion}/MyController")]
public class MyControllerController : ControllerBase
{
}
Now the only step that is missing, is routing the /latest/ path to the most recent controller of the given type. I have not found a solution to this yet, but this should be doable.

How to add Json Formatters to MvcCore?

I'm following the next tutorial of IdentityServer4 implementation for API , but I can't call the method AddJsonFormatters() to services.AddMvcCore().
I'm currently configuring the API from an empty template in ASP.NET Core 3.0.0
I have added NuGet package Microsoft.AspNetCore.Mvc.Formatters.Json with no results.
Also, I understand that using AddMvc() instead of AddMvcCore() would be a partial solution but I can't use AddAuthorization() on AddMvc()
//code extracted from the link
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
}
}
This is the error message I see above:
'IMvcCoreBuilder' does not contain a definition for
'AddJsonFormatters' and no accessible extension method
'AddJsonFormatters' accepting a first argument of type
'IMVCoreBuilder' could be found (are you using a missing directive or
an assembly reference?)
Is this the method? Should I send an MVCCoreBuilder? How do I do that? MvcJsonMvcCoreBuilderExtensions.AddJsonFormatters Method
When you call services.AddMvc() you get an IMvcBuilder.
if you want to add more output or input formatters, the IMvcBuilder has an extension method that you can call AddMvcOptions bellow you have an example of an XmlDataContractSerializerOutputFormatter that was added
mvcBuilder.AddMvcOptions(options =>
{
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options));
Mvc already has a JsonOutputFormatter ,so inside of the AddMvcOptions you can get it and also and add your own custom mediatypes if you need it.
var jsonOutputFormatter = options.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault();
if (jsonOutputFormatter != null)
{
jsonOutputFormatter.SupportedMediaTypes.Add(HttpMediaTypes.Vnd+json.all);
jsonOutputFormatter.SupportedMediaTypes.Add(HttpMediaTypes.ApplicationOctetStream);
}
As I understood, there is not class MvcJsonMvcCoreBuilderExtensions in .NET Core 3.0 yet.
Eventually I just added -f parameter when I was created the Api project:
dotnet new web -n Api -f netcoreapp2.2
instead of
dotnet new web -n Api
It makes the Api project for .NET Core 2.2 so you can read the tutorial.
I was having the same issue. I found that I was on the wrong tutorial for implementing it with the current versions of dotnet core. The comment made by -AnorZaken helped:
Check the tutorial again, it has been updated to NetCore3
Look at the top of the sidebar on the tutorial page. If it says "Release" under the IdentityServer4 title, that won't work.
There is a dropdown at the bottom of the sidebar where you can select "3.1" instead.
Use this:
If MVC
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver());
.....
.....
if it is API than use
services.AddControllersWithViews().AddNewton ......

ODataClient MaxProtocolVersion V3

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.

Categories

Resources