сделать tnet основной OData 3.1 - не возвращает правильные записи - PullRequest
0 голосов
/ 13 апреля 2020

Я экспериментирую с OData с do tnet 3.1.

У меня есть SPA, который отправляет следующий URL:

https://localhost:44301/api/Client/Index?$top=10&$orderby=ClientNo%20desc,ClientLastName%20asc

Приложение получает запрос GET и контроллер однако обрабатывает его, не возвращая первые 10 записей, но все 146.

enter image description here

Как видите, результат равен 146.

Мой контроллер выглядит следующим образом:

using System.Linq;
using System.Threading.Tasks;

using JobsLedger.API.ControllerServices.API.App.ClientService.Interfaces;
using JobsLedger.DATA;
using JobsLedger.MODELS.API.App.Client;
using Microsoft.AspNet.OData;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JobsLedger.API.Controllers.API.App {
    [ApiController]
    [Route("api/[controller]")]
    //[AllowAnonymous]
    [Authorize(Roles = "TenantAdmin,Admin,Employee")]
    public class ClientController :ODataController // ControllerBase 
    {
        private readonly IClientServices _clientServices;
        private readonly DATAContext _context;

        public ClientController(IClientServices clientServices, DATAContext context) {
            _clientServices = clientServices;
            _context = context;
        }


        // This is the default client index option.
        [EnableQuery]
        [HttpGet("Index", Name = "ClientIndex")]
        public IActionResult Get() {

            var result = _context.Clients.ToList();
            return new OkObjectResult(result);
        }
    }
}

Я использовал настройку, как описано в статье Хасана Хабиба .

Вот мой StartUp.cs

using System;
using System.Diagnostics;
using System.Resources;
using System.Security.Claims;
using System.Text;

using JobsLedger.API.ControllerServices.API.App.ClientService;
using JobsLedger.API.ControllerServices.API.App.ClientService.Interfaces;
using JobsLedger.API.ControllerServices.API.App.JobService;
using JobsLedger.API.ControllerServices.API.App.JobService.Interfaces;
using JobsLedger.API.ControllerServices.Catalog.TenantService;
using JobsLedger.API.ControllerServices.Catalog.TenantService.Interfaces;
using JobsLedger.API.ControllerServices.Catalog.UserService;
using JobsLedger.API.ControllerServices.Catalog.UserService.Abstract;
using JobsLedger.API.ControllerServices.Common;
using JobsLedger.API.ControllerServices.Common.Interfaces;
using JobsLedger.AUTHORISATION;
using JobsLedger.AUTHORISATION.API.SessionMiddleware;
using JobsLedger.AUTHORISATION.API.SessionMiddleware.Interfaces;
using JobsLedger.AUTHORISATION.Interfaces;
using JobsLedger.CATALOG;
using JobsLedger.CATALOG.Repositories;
using JobsLedger.CATALOG.Repositories.Interfaces;
using JobsLedger.DATA;
using JobsLedger.DATA.ENTITIES;
using JobsLedger.DATA.Interfaces;
using JobsLedger.DATA.Repositories;
using JobsLedger.DATA.Repositories.Interfaces;
using JobsLedger.INITIALISATION;
using JobsLedger.INITIALISATION.CATALOG.Initialisations;
using JobsLedger.INITIALISATION.CATALOG.Initialisations.Interfaces;
using JobsLedger.INITIALISATION.DATA.Initialisations;
using JobsLedger.INITIALISATION.DATA.Initialisations.Interfaces;
using JobsLedger.INTERFACES;
using JobsLedger.TESTDATA;
using JobsLedger.TESTDATA.Interfaces;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.OData.Edm;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;


[assembly: NeutralResourcesLanguage("en")]

namespace JobsLedger.API {
    public class Startup {
        private const string SecretKey = "needtogetthisfromenvironment";

        private readonly SymmetricSecurityKey
        _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));

        private readonly IWebHostEnvironment _env;
        private readonly IConfiguration _configuration;  // { get; }

        public Startup(IWebHostEnvironment env, IConfiguration configuration) {
            _env = env;
            _configuration = configuration;
        }


        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services) {
            // Add framework services.
            services.AddCors();
            services.AddControllers().AddNewtonsoftJson();

            services.AddOData();

            services.AddOptions();

            services.AddDbContext<CATALOGContext>(options => options.UseLazyLoadingProxies().UseSqlServer(_configuration.GetConnectionString("CatalogConnection"), b => b.MigrationsAssembly("JobsLedger.CATALOG")));
            services.AddDbContext<DATAContext>(options => options.UseLazyLoadingProxies().UseSqlServer(_configuration.GetConnectionString("TenantDbConnection"), a => a.MigrationsAssembly("JobsLedger.DATA")));

            // Make authentication compulsory across the board (i.e. shut
            // down EVERYTHING unless explicitly opened up).
            // Use policy auth.
            services.AddAuthorization(options => {
                options.AddPolicy("TenantAdmin", policy => policy.RequireClaim(ClaimTypes.Role, "TenantAdmin"));
                options.AddPolicy("Admin", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
                options.AddPolicy("Employee", policy => policy.RequireClaim(ClaimTypes.Role, "Employee"));
            });

            //services.ConfigureApplicationInjection();
            //var tokenOptions = _configuration.GetSection("Authentication").Get<JwtIssuerOptions>();
            var jwtAppSettingOptions = _configuration.GetSection(nameof(JwtIssuerOptions));

            // Configure JwtIssuerOptions
            services.Configure<JwtIssuerOptions>(options => {
                options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
                options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
                options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
            });

            // Get jwt options from app settings

            services.AddAuthentication(x => {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options => {
                options.TokenValidationParameters = new TokenValidationParameters {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    RequireExpirationTime = true,
                    ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
                    ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
                    IssuerSigningKey = _signingKey,
                    ClockSkew = TimeSpan.Zero
                };
            });

            services
                .AddDistributedMemoryCache()
                .AddSession()

                // Repositories - DATA
                .AddScoped<IClientDATARepository, ClientDATARepository>()
                .AddScoped<ILoggingDATARepository, LoggingDATARepository>()
                .AddScoped<IJobDATARepository, JobDATARepository>()
                .AddScoped<IBrandDATARepository, BrandDATARepository>()
                .AddScoped<ITypeDATARepository, TypeDATARepository>()
                .AddScoped<IStateDATARepository, StateDATARepository>()
                .AddScoped<IStatusDATARepository, StatusDATARepository>()
                .AddScoped<ISuburbDATARepository, SuburbDATARepository>()
                .AddScoped<ICounterDATARepository, CounterDATARepository>()

                // Repositories - CATALOG
                .AddScoped<ITenantCATALOGRepository, TenantCATALOGRepository>()
                .AddScoped<IUserCATALOGRepository, UserCATALOGRepository>()
                .AddScoped<IRoleCATALOGRepository, RoleCATALOGRepository>()
                .AddScoped<ICounterCATALOGRepository, CounterCATALOGRepository>()
                .AddScoped<ISuburbCATALOGRepository, SuburbCATALOGRepository>()
                .AddScoped<IStateCATALOGRepository, StateCATALOGRepository>()

                // Business services
                // Services - API
                .AddScoped<IClientServices, ClientServices>()
                .AddScoped<IJobServices, JobServices>()

                //Services - Catalog
                .AddScoped<ITenantServices, TenantServices>()
                .AddScoped<IUserServices, UserServices>()
                //.AddScoped<IUserValidateService, UserValidateService>()

                // Services - Shared
                .AddScoped<IAddressDropdownServices, AddressDropdownServices>()

                //Services - Auth
                .AddScoped<ICryptoService, CryptoService>()
                .AddScoped<IJwtServices, JwtServices>()
                .AddScoped<IUserSession, UserSession>()
                .AddScoped<ISessionServices, SessionServices>()

                // CATALOG services - Initialisations.
                .AddScoped<ICATALOGCounterInitialiser, CATALOGCounterInitialiser>()
                .AddScoped<ICATALOGStateInitialiser, CATALOGStateInitialiser>()
                .AddScoped<ICATALOGSuburbInitialiser, CATALOGSuburbInitialiser>()
                .AddScoped<IRoleInitialiser, CATALOGRoleInitialiser>()
                .AddScoped<ICATALOGTenantAndUserInitialisations, CATALOGTenantAndUserInitialiser>()

                // DATA Services - Initialisations.
                .AddScoped<IBrandInitialiser, BrandInitialiser>()
                .AddScoped<IDATACounterInitialiser, DATACounterInitialiser>()
                .AddScoped<IDATAStateInitialiser, DATAStateInitialiser>()
                .AddScoped<IDATASuburbInitialiser, DATASuburbInitialiser>()
                .AddScoped<INoteTypeInitialiser, NoteTypeInitialiser>()
                .AddScoped<IStatusInitialiser, StatusInitialiser>()
                .AddScoped<ITypeInitialiser, TypeInitialiser>()
                .AddScoped<IVisitTypeInitialiser, VisitTypeInitialiser>()

                // TESTDATA Services - Initialisations.
                .AddScoped<ITESTDATAInitialisations, TESTDATAInitialisations>()
                .AddScoped<ITESTDATATenantAndUserInitialisations, TESTDATATenantAndUserInitialisations>()

                .AddTransient<IDATAContextFactory, DATAContextFactory>()
                .AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // For getting the user.
        }

        public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
            }
            else {
                app.UseExceptionHandler("/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.UseExceptionHandler(options => {
                options.Run( async context => {
                    var ex = context.Features.Get<IExceptionHandlerPathFeature>();
                    if (ex?.Error != null) {
                         Debugger.Break();
                    } 

                });
            });

            app.UseRouting();

            // global cors policy
            app.UseCors(x => x
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            app.UseAuthentication();
            app.UseHttpsRedirection();
            app.UseFileServer();
            app.UseStaticFiles();
            app.UseSession();
            app.UseAuthorization();

            app.UseConfigureSession();

            //app.EnsureCATALOGMigrationAndInitialise();
            //app.EnsureDATAMigrationsAndInitialise();

            app.UseEndpoints(endpoints => {
                endpoints.MapControllers();
                endpoints.EnableDependencyInjection();
                endpoints.Select().Filter().OrderBy().Expand().Count().MaxTop(10);
            });
        }
    }
}

Я использую маршрутизацию конечной точки.

Мой запрос OData:

$top=10&$orderby=ClientNo desc,ClientLastName asc

Замечу, что ряд руководств по использованию OData

var result = _context.Clients.ToList();

или версия в том же духе, но в моем случае она не принимает топ-10 как запрошено.

Я понятия не имею, почему это не работает, и надеюсь, что кто-то, кто намного лучше знаком с OData, сможет обнаружить проблему ..

Кстати, запрос OData исходит от реализации Slickgrid, так что все должно быть в порядке. ..

Кто-нибудь заметит простую проблему с моей реализацией или запросом et c ..

...