
Одним из наиболее важных аспектов разработки программного обеспечения является быстрое создание прототипов. Для большинства служб необходимы по крайней мере некоторые операции CRUD, и большинство приложений можно описать как приложения, управляемые данными. API, которые я пишу, в основном берут данные из базы данных и возвращает их клиенту в виде JSON. OdataToEntity — это инструмент, который генерирует API из базы данных и устраняет необходимость в написании отдельного REST API.
В этой статье я покажу, как OdataToEntity может помочь устранить скучную работу по написанию CRUD методов. В прошлой статье я рассказывал как создать OData сервис с минимальным кодированием, в этой статье я покажу как сделать это вообще без написания кода.
Эта функциональность доступна в проекте OdataToEntity.EfCore.DynamicDataContext, который является частью библиотеки OdataToEntity. Реализован пример HTTP сервера в виде консольной программы, принимающей на вход строку подключения к базе данных. Поддерживаются базы данных: MySql, PostgreSql, Sql Server. Помимо таблиц и CRUD операций над ними, доступны представления, хранимые процедуры и функции.
Описание HTTP сервера
Исходный код сервера доступен на GitHub.
Настройка сервера осуществляется через файл конфигурации. Это стандартный файл Asp .net core в который добавлен ключ OdataToEntity
"OdataToEntity": { "BasePath" : "api", "Provider": "sqlserver", "ConnectionString": "Server=.\\sqlexpress;Initial Catalog=OdataToEntity;Trusted_Connection=Yes;", "UseRelationalNulls": true, "InformationSchemaMappingFileName": "InformationSchemaMapping.json" }
«BasePath» — базовый путь в URL сервера.
«Provider» — тип базы данных, возможные значения mysql, postgresql, sqlserver.
«ConnectionString» — строка подключения к базе данных.
«UseRelationalNulls» — указывает, следует ли использовать семантику реляционной базы данных
при сравнении нулевых значений.
«InformationSchemaMappingFileName» — дополнительная настройка отображения базы данных в API.
Программа автоматически обнаруживает процедуры, функции, отношения между таблицами базы данных, проверяя их внешние ключи. Я использую это для встраивания отношений в схему OData сервиса. Для дополнительной настройки имен используется файл InformationSchemaMapping.json это сериализованный класс InformationSchemaMapping.
Ключ «Operations» описывает хранимые процедуры и функции, «Tables» — таблицы и представления. Свойства «DbName» — имя в базе, «EdmName» — имя в сервисе, «Exclude» исключает объект базы и сервиса. Если хранимая процедура/функция возвращает таблицу, то имя таблицы надо задать в свойстве «ResultTableDbName». Для изменения имени навигационного свойства нужно использовать ключ «Navigations», где свойство «TargetTableName» указывает на целевую таблицу навигационного свойства, а «NavigationName» — его имя. Если таблица содержит несколько внешних ключей на одну и туже таблицу, то для различения этих навигационных свойств вместо «TargetTableName» необходимо задать «ConstraintName» — имя внешнего ключа базы данных. Для свойства многие-ко-многим нужно задать «ManyToManyTarget» — имя целевой таблицы ( дополнительную информацию о реализации многие-ко-многим смотрите по этой ссылке ).
Пример кода
Если вам необходимо использовать этот функционал в своем коде, добавьте ссылку на проект OdataToEntity.EfCore.DynamicDataContext
//Load our schema mappings (optional) InformationSchemaMapping informationSchemaMapping = GetMappings(); //create database schema var optionsFactory = new DynamicSchemaFactory("sqlserver", @"Server=.\sqlexpress;Initial Catalog=OdataToEntity;Trusted_Connection=Yes;"); ProviderSpecificSchema providerSchema = optionsFactory.CreateSchema(useRelationalNulls: true); IEdmModel dynamicEdmModel; //create database information_schema provider using (var metadataProvider = providerSchema.CreateMetadataProvider(informationSchemaMapping)) { //create ef entity types manager DynamicTypeDefinitionManager typeDefinitionManager = DynamicTypeDefinitionManager.Create(metadataProvider); //Create adapter data access var dataAdapter = new DynamicDataAdapter(typeDefinitionManager); //Build OData edm model dynamicEdmModel = dataAdapter.BuildEdmModel(metadataProvider); } //Create query parser var parser = new OeParser(new Uri("http://dummy"), dynamicEdmModel); //Query var uri = new Uri("http://dummy/Orders?$expand=Customer,Items&$orderby=Id"); //The result of the query var stream = new MemoryStream(); //Execute query await parser.ExecuteGetAsync(uri, OeRequestHeaders.JsonDefault, stream, CancellationToken.None); stream.Position = 0; //Get result as string Console.WriteLine(new StreamReader(stream).ReadToEnd());
Как это работает
По представлениям information_schema строится контекст Entity Framework. Сущности контекста являются наследниками абстрактного класса DynamicType. Этот класс накладывает ограничения на общее количество столбцов в таблице, их не должно быть больше 50. Количество навигационных свойств не должно превышать для свойств со стороны первичного ключа 50, со стороны внешнего ключа 30.
Общее количество таблиц и представлений ограничено 110, это количество ограничено реализациями класса DynamicType. Вы можете увеличить количество свойств или классов, добавив их в исходный код.
По контексту Entity Framework строится схема OData, как уже было описано в в моей предыдущей статье. Эта схема необходима для трансляции запроса в дерево выражений (expression tree), которое передается в контекст EntityFramework.
Структура исходного кода
Солюшен — sln\OdataToEntity.Test.DynamicDataContext.sln
Проект — source\OdataToEntity.EfCore.DynamicDataContext
HTTP сервер — test\OdataToEntity.Test.DynamicDataContext.AspServer
Тесты — OdataToEntity.Test.DynamicDataContext
Sql скрипты тестовой базы — test\sql_scripts
ссылка на оригинал статьи https://habr.com/ru/post/459656/
Добавить комментарий