Мне нужно предоставить конечную точку, совместимую с 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 "".
В любом случае, я удивлен, что в Интернете нет больше информации об этом, возможно, ядро сейчас слишком новое.Любая помощь будет принята с благодарностью