Предоставление сервисов EF Core CRUD через ODATA в точечной базовой сети API - PullRequest
0 голосов
/ 08 июля 2019

Мне нужно предоставить конечную точку, совместимую с OData V4, которая предоставит полный набор функций для нескольких баз данных.У меня есть отдельный проект, который я использую для создания всех библиотек EF Core для отдельных баз данных, и я пытаюсь добиться следующего: добавить dll базы данных через nuget, добавить пространство имен (возможно, через файл конфигурации), а затемэй presto, приложение будет создавать и выставлять сервисы CRUD для этих баз данных (DbContext)

До сих пор мне удалось создать универсальный контроллер, который затем создается "на лету" на основе статического списка конфигураций(только для тестирования) и все работает правильно.Однако, похоже, он не поддерживает весь массив функциональных возможностей OData.Фильтрация работает, «Выбор» работает, но данные не возвращаются в обычном обернутом объекте OData с метаданными и ссылками на подкачку.Кроме того, $ count = true ничего не делает

[Produces("application/json")]
[Authorize]
[ODataRoutePrefix("odata/[controller]")]
public class GenericController<TContext, TEntity> : ODataController
    where TContext : DbContext
    where TEntity : class
 {
    private TContext context;

    public GenericController(TContext context)
    {
        this.context = context;
    }

    [ODataRoute]
    [EnableQuery(PageSize = 20, AllowedQueryOptions = AllowedQueryOptions.All)]
    public IQueryable<TEntity> Get() => context.Set<TEntity>().AsQueryable();
}

Затем я регистрирую GenericControllerFeatureProvider (для динамического создания и регистрации контроллера правильного типа)

public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
        foreach (var entityConfig in Startup.ControllerTypes)
        {
            var entityType = entityConfig.DataType;
            var typeName = entityType.Name + "Controller";
            if (!feature.Controllers.Any(t => t.Name == typeName))
            {
                var controllerType = typeof(GenericController<,>).MakeGenericType(entityConfig.DataContext, entityType).GetTypeInfo();

                feature.Controllers.Add(controllerType);
            }
        }
}

"Startup.ControllerTypes"статическая коллекция типов, которые я хочу зарегистрировать.Пока это просто жестко запрограммировано, но в конечном итоге будет через config / Db

public Startup(IConfiguration configuration)
{

        ControllerTypes = new List<GenericTypeMap>();
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxQueue", DataContext = typeof(QueueDbContext), DataType = typeof(EventLog) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxQueue", DataContext = typeof(QueueDbContext), DataType = typeof(EventProcessor) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxQueue", DataContext = typeof(QueueDbContext), DataType = typeof(EventProcessorConfig) });

        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(Job) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(JobDetail) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(JobLog) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(JobSchedule) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(JobScheduleType) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(JobType) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(JobTypeField) });
        ControllerTypes.Add(new GenericTypeMap { SegmentPath = "wrxScheduling", DataContext = typeof(SchedulingDbContext), DataType = typeof(EventProcessorConfig) });

        Configuration = configuration;
}

. Я также регистрирую GenericControllerModelConvention (для динамической настройки маршрутизации)

public void Apply(ControllerModel controller)
{
        if (!controller.ControllerType.IsGenericType || controller.ControllerType.GetGenericTypeDefinition() != typeof(GenericController<,>))
        {
            return;
        }

        var contextType = controller.ControllerType.GenericTypeArguments[0];
        var entityType = controller.ControllerType.GenericTypeArguments[1];
        controller.ControllerName = entityType.Name + "Controller";
        controller.RouteValues["Controller"] = entityType.Name;


        //Force override the routing for the generic controller implementations
        foreach (var selectorModel in controller.Selectors)
        {
            selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute($"api/odata/{contextType.Name.Replace("DbContext", string.Empty)}/[controller]"));
        }
}

И, наконец,startup.cs

В ConfigureServices ....

 services.AddOData();
        services.AddODataQueryFilter();

        services.AddMvc()
            .AddMvcOptions(o => o.EnableEndpointRouting = false)
            .AddMvcOptions(o => o.Conventions.Add(new GenericControllerModelConvention()))
            .ConfigureApplicationPartManager(c =>
            {
                c.FeatureProviders.Add(new GenericControllerFeatureProvider());
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

В Configure ....

app.UseMvc(routing =>
        {
            routing.EnableDependencyInjection();
            routing.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
        });

Ух ты, сколько циклов прыгать.Но я думаю, что реальная проблема заключается в следующем: я не использую метод «MapODataServiceRoute», о котором все примеры рассказывают, и поэтому я нигде не генерирую IEdmModel.Поэтому я думаю, что когда я генерирую маршрутизацию на лету, она просто применяет базовый ApiRouting, а не ODataRouting, и именно поэтому работает только «базовый» материал odata.

На самом деле, мое единственное требование - я не хочу создавать контроллер для каждого DbSet в моих базах данных (это было бы как более 300 сотен контроллеров ... вы псих!)Я определенно не собираюсь управлять этим.И в любом случае, это похоже на серьезное дублирование.

Так что, как я вижу, я рядом.У меня есть 2 варианта.Либо мне просто нужно исправить мою динамическую маршрутизацию, чтобы использовать ODataRoute вместо RouteAttribute (не думайте, что это так), либо мне нужно сгенерировать IEdmModel "на лету" из моего DbContext, а затем сопоставить маршрутизацию с помощью метода MapODataServiceRoute "".

В любом случае, я удивлен, что в Интернете нет больше информации об этом, возможно, ядро ​​сейчас слишком новое.Любая помощь будет принята с благодарностью

...