OData Controller withing Asp.Net Core MVC application

1.6k views Asked by At

I'm working on a project in ASP .NET Core 3.1 MVC now I want to add some API controllers to return list of objects. For this I want to use OData Controller version 8.0.0 so I can get quarriable data to improve performance on large data tables

I'm new in ASP .NET Core and OData. can anybody explain how to configure my project's Startup file so I can run both MVC and OData controllers same time. Kindly share some example code

2

There are 2 answers

1
Anjum On BEST ANSWER

I have managed to fix my issue to run Web Application which exposes OData APIs Issue was in Startup.cs file

I'm using Asp.Net Core 3.1 and Microsoft.AspNetCore.OData v7.3.0

my Startup.cs file code is:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));


        services.AddIdentity<AppUser, AppRole>(opt =>
        {
            opt.User.RequireUniqueEmail = true;
        })
            //.AddDefaultUI(UIFramework.Bootstrap4)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        //add services
        services.RegisterServices();

        services.AddScoped<ViewRendererService>();

        services.AddMvc()
            .AddMvcOptions(options => options.EnableEndpointRouting = false)
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

        services.AddOData();

        services.AddRouting();
        services.AddControllersWithViews();
        services.AddRazorPages();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext dataContext)
    {
        if (env.EnvironmentName == "Development")
        {
            dataContext.Database.Migrate();
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseDeveloperExceptionPage();
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseAuthentication();

        app.UseRequestLocalization();

        app.UseMvc(routes =>
        {
            routes.Select().Filter().OrderBy().Expand().Count().SkipToken().MaxTop(null);
            routes.MapODataServiceRoute("odata", "api", GetEdmModel());
            routes.MapRoute(
                name: "areas",
                template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
            );

            routes.MapRoute(
                name: "Finance",
                template: "{area:exists}/{controller=Account}/{action=Index}/{id?}"
            );

            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

    }
    private static IEdmModel GetEdmModel()
    {
        var builder = new ODataConventionModelBuilder();
        builder.EntitySet<Product>("ProductApi");
        builder.EntitySet<ProductUOM>("ProductUomApi");

        ActionConfiguration action = builder.EntityType<Product>().Action("GetUOM");
        action.Parameter<long>("id");
        action.ReturnsCollectionFromEntitySet<Product>("Product");

        return builder.GetEdmModel();
    }
}

Hope this will help others

1
Tiny Wang On

Firstly, you have a MVC project, since MVC project can also expose API, so OData should also work for MVC project. Firstly, assuming you've integrate ef core and in my workaround, I followed this document to create database and data management view for a model.

Then let's add OData. Install this nuget package: Microsoft.AspNetCore.OData, modify your startup.cs file, please see the Configuration and GetEdmModel method.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using WebMvcNet5.Data;
using WebMvcNet5.Models;
using Microsoft.OData.ModelBuilder;
using Microsoft.AspNetCore.OData;
using Microsoft.OData.Edm;

namespace WebMvcNet5
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            services.AddControllers().AddOData(opt => opt.EnableQueryFeatures().AddRouteComponents("odata", GetEdmModel()));
            services.AddDbContext<WebMvcNet5Context>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("WebMvcNet5Context")));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        private static IEdmModel GetEdmModel()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            //My model is Movie, and what I set "GetMovie" here means I need to create a controller named "GetMovieController"
            builder.EntitySet<Movie>("GetMovie");
            return builder.GetEdmModel();
        }
    }
}

Then this is my controller:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using WebMvcNet5.Data;

namespace WebMvcNet5.Controllers
{
    [Route("odata/[Controller]")]
    public class GetMovieController : Controller
    {
        private readonly WebMvcNet5Context _context;

        public GetMovieController(WebMvcNet5Context context)
        {
            _context = context;
        }

        [EnableQuery]
        public IActionResult Get()
        {
            return Ok(_context.Movie);
        }
    }
}

My test result:

enter image description here