Одним из наиболее важных аспектов разработки программного обеспечения является быстрое создание прототипов. Для большинства служб необходимы по крайней мере некоторые операции 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();
//Configure context
var optionsBuilder = new DbContextOptionsBuilder<DynamicDbContext>();
optionsBuilder = optionsBuilder.UseSqlServer("Server=.\\sqlexpress;Initial Catalog=OdataToEntity;Trusted_Connection=Yes;");
IEdmModel dynamicEdmModel;
//create database schema
using (ProviderSpecificSchema providerSchema = new SqlServerSchema(optionsBuilder.Options))
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());
Структура исходного кода
Солюшен — sln\OdataToEntity.Test.DynamicDataContext.sln
Проект — source\OdataToEntity.EfCore.DynamicDataContext
HTTP сервер — test\OdataToEntity.Test.DynamicDataContext.AspServer
Тесты — OdataToEntity.Test.DynamicDataContext
Sql скрипты тестовой базы — test\sql_scripts