diff --git a/Controllers/XBaseEntityController.cs b/Controllers/XBaseRepositoryController.cs similarity index 98% rename from Controllers/XBaseEntityController.cs rename to Controllers/XBaseRepositoryController.cs index 168587a..014e79d 100644 --- a/Controllers/XBaseEntityController.cs +++ b/Controllers/XBaseRepositoryController.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using xCommons.Configurations; -using xCommons.Controllers; using xCommons.Extensions; using xCommons.Providers; using xExceptions.Constants; @@ -14,7 +13,8 @@ using xDataService.Interfaces; using System.Linq; using Microsoft.EntityFrameworkCore.Query; using System.Threading; -using SQLitePCL; +using xCommons.Controllers; +using Microsoft.AspNetCore.Authorization; namespace xDataService.Controllers { @@ -23,7 +23,8 @@ namespace xDataService.Controllers /// /// /// - public abstract class XBaseEntityController : XBaseController, IXEntityControllerActions + [AllowAnonymous] + public abstract class XBaseRepositoryController : XBaseApiV1Controller, IXBaseRepositoryController where TEntity : XBaseEntity { // @@ -37,8 +38,8 @@ namespace xDataService.Controllers // #region Constructor ... - protected XBaseEntityController( - ILogger> logger, + protected XBaseRepositoryController( + ILogger> logger, XAppConfiguration appConfiguration, XValidationProvider validationProvider, IXBaseRepository repository, diff --git a/Controllers/XBaseEntityDtoController.cs b/Controllers/XBaseServiceController.cs similarity index 98% rename from Controllers/XBaseEntityDtoController.cs rename to Controllers/XBaseServiceController.cs index fc477c1..0cb1fae 100644 --- a/Controllers/XBaseEntityDtoController.cs +++ b/Controllers/XBaseServiceController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore.Query; using Microsoft.Extensions.Logging; @@ -23,7 +24,8 @@ namespace xDataService.Controllers /// /// /// - public abstract class XBaseEntityDtoController : XBaseController, IXDomainControllerActions + [AllowAnonymous] + public abstract class XBaseServiceController : XBaseApiV1Controller, IXBaseServiceController where TEntity : XBaseEntity where TDto : XBaseEntityDto { @@ -36,8 +38,8 @@ namespace xDataService.Controllers // #region Constructor ... - protected XBaseEntityDtoController( - ILogger> logger, + protected XBaseServiceController( + ILogger> logger, XAppConfiguration appConfiguration, XValidationProvider validationProvider, IXBaseRepositoryService repositoryService, diff --git a/DI/XDIHelperExtension.cs b/DI/XDIHelperExtension.cs index f8e6cc5..142c04f 100644 --- a/DI/XDIHelperExtension.cs +++ b/DI/XDIHelperExtension.cs @@ -353,9 +353,8 @@ namespace xDataService.DI if (isValid) { // - string graphPath = $"{helper.GetGraphQLPath()}"; - // string graphPath = $"{dataServiceConfiguration.GraphQLBasePath}{helper.GetGraphQLPath()}"; - graphPath = $"{helper.GetGraphQLPath()}"; + var graphPath = $"{helper.GetGraphQLPath()}"; + var graphBasePath = $"{dataServiceConfiguration.GraphQLBasePath}"; typeof(XDataServiceHelper).InvokeGenericMethod( methodName: "UseXGraphQL", runtimeTypes: [ @@ -370,9 +369,9 @@ namespace xDataService.DI ], args: [ source, + graphBasePath, graphPath, - withPlayground, - null + withPlayground ] ); diff --git a/Extensions/XRepositoryDescriptorExtensions.cs b/Extensions/XRepositoryDescriptorExtensions.cs index a9bb8ef..6428043 100644 --- a/Extensions/XRepositoryDescriptorExtensions.cs +++ b/Extensions/XRepositoryDescriptorExtensions.cs @@ -1,11 +1,7 @@ using xCommons.Extensions; using xDataService.Db; -using xDataService.EFRepositories; -using xDataService.Events; -using xDataService.InMemRepositories; using xDataService.Interfaces; using xDataService.Models; -using xDataService.MongoRepositories; using xDataService.Providers; using xModels.Base; diff --git a/GraphQL/XBaseGraphObjectType.cs b/GraphQL/XBaseGraphObjectType.cs index 96c47a5..b7e1818 100644 --- a/GraphQL/XBaseGraphObjectType.cs +++ b/GraphQL/XBaseGraphObjectType.cs @@ -7,11 +7,11 @@ namespace xDataService.GraphQL /// Base Entity GraphType Object ... /// /// - public abstract class XBaseGraphObjectType : ObjectGraphType - where T : XBaseEntity { + public abstract class XBaseGraphObjectType : ObjectGraphType + where TEntity : XBaseEntity { public XBaseGraphObjectType () { // - Name = $"{typeof(T).Name}GraphType"; + Name = $"{typeof(TEntity).Name}GraphType"; // Field (x => x.Id); diff --git a/GraphQL/XBaseGraphQLSubscription.cs b/GraphQL/XBaseGraphQLSubscription.cs index 9005c93..5e325b6 100644 --- a/GraphQL/XBaseGraphQLSubscription.cs +++ b/GraphQL/XBaseGraphQLSubscription.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using GraphQL.Resolvers; using GraphQL.Types; using xCommons.Extensions; -using xDataService.Events; +using xDataService.Providers; using xDataService.Interfaces; using xDataService.Models; using xModels.Base; diff --git a/Helpers/XDataServiceHelper.cs b/Helpers/XDataServiceHelper.cs index 6b451ed..0be05e5 100644 --- a/Helpers/XDataServiceHelper.cs +++ b/Helpers/XDataServiceHelper.cs @@ -2,6 +2,7 @@ using System; using GraphQL; using GraphQL.Server; using GraphQL.Server.Ui.Playground; +using GraphQL.Server.Ui.Voyager; using GraphQL.Types; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; @@ -12,14 +13,10 @@ using MySql.EntityFrameworkCore.Extensions; using xCommons.Extensions; using xDataService.Constants; using xDataService.Db; -using xDataService.EFRepositories; -using xDataService.Events; using xDataService.Extensions; using xDataService.GraphQL; -using xDataService.InMemRepositories; using xDataService.Interfaces; using xDataService.Models; -using xDataService.MongoRepositories; using xDataService.Providers; using xExceptions.Constants; using xModels.Base; @@ -1197,6 +1194,7 @@ namespace xDataService.Helpers /// /// /// + /// /// /// /// @@ -1211,9 +1209,9 @@ namespace xDataService.Helpers TGraphSchema >( IApplicationBuilder source, + string graphBasePath, string graphPath, - bool withPlayGround = false, - GraphQLPlaygroundOptions options = null + bool withPlayGround = false ) where TGraphSchema : Schema where TGraphKey : IGraphType @@ -1231,15 +1229,18 @@ namespace xDataService.Helpers if (withPlayGround) { // - if (options.IsNull()) + source.UseGraphQLPlayground(new GraphQLPlaygroundOptions { - options = new GraphQLPlaygroundOptions(); - } - options.Path = "/ui/playground"; - options.GraphQLEndPoint = graphPath; + Path = "/ui/playground", + GraphQLEndPoint = graphBasePath + }); // - source.UseGraphQLPlayground(options); + source.UseGraphQLVoyager(new GraphQLVoyagerOptions + { + Path = "/ui/voyager", + GraphQLEndPoint = graphBasePath + }); } } #endregion diff --git a/Interfaces/IXEntityControllerActions.cs b/Interfaces/IXBaseRepositoryController.cs similarity index 98% rename from Interfaces/IXEntityControllerActions.cs rename to Interfaces/IXBaseRepositoryController.cs index 0435a42..2320b05 100644 --- a/Interfaces/IXEntityControllerActions.cs +++ b/Interfaces/IXBaseRepositoryController.cs @@ -16,7 +16,7 @@ namespace xDataService.Interfaces /// /// /// - public interface IXEntityControllerActions + public interface IXBaseRepositoryController where TEntity : XBaseEntity { // diff --git a/Interfaces/IXDomainControllerActions.cs b/Interfaces/IXBaseServiceController.cs similarity index 98% rename from Interfaces/IXDomainControllerActions.cs rename to Interfaces/IXBaseServiceController.cs index 5e2a7ef..9e5928a 100644 --- a/Interfaces/IXDomainControllerActions.cs +++ b/Interfaces/IXBaseServiceController.cs @@ -16,7 +16,7 @@ namespace xDataService.Interfaces /// /// /// - public interface IXDomainControllerActions + public interface IXBaseServiceController where TEntity : XBaseEntity where TDto : XBaseEntityDto { diff --git a/Interfaces/IXKeyGenerator.cs b/Interfaces/IXKeyGenerator.cs index 58c77b2..6720d15 100644 --- a/Interfaces/IXKeyGenerator.cs +++ b/Interfaces/IXKeyGenerator.cs @@ -4,17 +4,12 @@ using xModels.Base; namespace xDataService.Interfaces { - /// - /// Non Generic Key Generator Interface ... - /// - public interface IXKeyGenerator { } - /// /// Typed Base Key Generator ... /// /// /// - public interface IXKeyGenerator : IXKeyGenerator + public interface IXKeyGenerator where TEntity : XBaseEntity { bool IsEmpty(TKey id); diff --git a/Models/XRepositoryDescriptor.cs b/Models/XRepositoryDescriptor.cs index c7bb2f2..317c6cb 100644 --- a/Models/XRepositoryDescriptor.cs +++ b/Models/XRepositoryDescriptor.cs @@ -1,12 +1,8 @@ using System; using GraphQL.Types; using xDataService.Db; -using xDataService.EFRepositories; -using xDataService.Events; using xDataService.GraphQL; -using xDataService.InMemRepositories; using xDataService.Interfaces; -using xDataService.MongoRepositories; using xDataService.Providers; using xModels.Base; diff --git a/EFRepositories/XBaseEFRepository.cs b/Providers/XBaseEFRepository.cs similarity index 98% rename from EFRepositories/XBaseEFRepository.cs rename to Providers/XBaseEFRepository.cs index 4257659..6617307 100644 --- a/EFRepositories/XBaseEFRepository.cs +++ b/Providers/XBaseEFRepository.cs @@ -16,7 +16,7 @@ using xDataService.Models; using xModels.Base; using xModels.Dtos; -namespace xDataService.EFRepositories +namespace xDataService.Providers { /// /// a Base Repository Pattern Implementation Specially for EF DBs using @@ -629,14 +629,14 @@ namespace xDataService.EFRepositories { predicator = x => !x.Deleted; } - var result = await AsQueryable( + var result = (await AsQueryable( asNoTracking: true, orderBuilder: null, predicate: predicator, includeBuilder: includeBuilder ) - .Where(x => GetKey(x).ToString() == id.ToString()) - .FirstOrDefaultAsync(cancellationToken); + .ToListAsync(cancellationToken)) + .FirstOrDefault(x => GetKey(x).ToString() == id.ToString()); // return result; @@ -732,14 +732,14 @@ namespace xDataService.EFRepositories { predicator = x => !x.Deleted; } - var result = await AsQueryable( + var result = (await AsQueryable( asNoTracking: true, orderBuilder: null, predicate: predicator, includeBuilder: includeBuilder ) - .Where(predicate) - .FirstOrDefaultAsync(cancellationToken); + .ToListAsync(cancellationToken)) + .FirstOrDefault(predicate.Compile()); // return result; @@ -769,14 +769,14 @@ namespace xDataService.EFRepositories { predicator = x => !x.Deleted; } - var result = await AsQueryable( + var result = (await AsQueryable( asNoTracking: true, predicate: predicator, orderBuilder: orderBuilder, includeBuilder: includeBuilder ) - .Where(predicate) - .ToListAsync(cancellationToken); + .ToListAsync(cancellationToken)) + .Where(predicate.Compile()); // return result; @@ -888,15 +888,14 @@ namespace xDataService.EFRepositories { predicator = x => !x.Deleted; } - var result = await AsQueryable( + var result = (await AsQueryable( asNoTracking: true, orderBuilder: null, includeBuilder: null, predicate: predicator ) - .AnyAsync( - cancellationToken: cancellationToken, - predicate: x => GetKey(x).ToString() == id.ToString()); + .ToListAsync(cancellationToken)) + .Any(predicate: x => GetKey(x).ToString() == id.ToString()); // return result; diff --git a/Providers/XBaseEntityTypeConfiguration.cs b/Providers/XBaseEntityTypeConfiguration.cs new file mode 100644 index 0000000..968e8c3 --- /dev/null +++ b/Providers/XBaseEntityTypeConfiguration.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Configure an Entity Specifications ... + /// + /// + /// + public abstract class XBaseEntityTypeConfiguration : IEntityTypeConfiguration + where TEntity : XBaseEntity + { + public abstract void Configure(EntityTypeBuilder builder); + } +} \ No newline at end of file diff --git a/InMemRepositories/XBaseInMemoryRepository.cs b/Providers/XBaseInMemoryRepository.cs similarity index 99% rename from InMemRepositories/XBaseInMemoryRepository.cs rename to Providers/XBaseInMemoryRepository.cs index fec2bcf..8c6f6ff 100644 --- a/InMemRepositories/XBaseInMemoryRepository.cs +++ b/Providers/XBaseInMemoryRepository.cs @@ -15,7 +15,7 @@ using xDataService.Models; using xModels.Base; using xModels.Dtos; -namespace xDataService.InMemRepositories +namespace xDataService.Providers { /// /// a Base Repository Pattern Implementation Specially for InMemory Stores using diff --git a/MongoRepositories/XBaseMongoRepository.cs b/Providers/XBaseMongoRepository.cs similarity index 99% rename from MongoRepositories/XBaseMongoRepository.cs rename to Providers/XBaseMongoRepository.cs index b6ae8be..c678778 100644 --- a/MongoRepositories/XBaseMongoRepository.cs +++ b/Providers/XBaseMongoRepository.cs @@ -16,7 +16,7 @@ using xDataService.Models; using xModels.Base; using xModels.Dtos; -namespace xDataService.MongoRepositories +namespace xDataService.Providers { /// /// a Base Repository Pattern Implementation Specially for Mongo DBs using diff --git a/Events/XBaseRepositoryEvents.cs b/Providers/XBaseRepositoryEvents.cs similarity index 98% rename from Events/XBaseRepositoryEvents.cs rename to Providers/XBaseRepositoryEvents.cs index 2298d62..b0b0e7d 100644 --- a/Events/XBaseRepositoryEvents.cs +++ b/Providers/XBaseRepositoryEvents.cs @@ -5,7 +5,7 @@ using System.Reactive.Subjects; using xDataService.Interfaces; using xDataService.Models; -namespace xDataService.Events { +namespace xDataService.Providers { /// /// Some Events can be Raised on Some Actions Happens on Repositories ... /// this is the Base Events Class ... diff --git a/README.md b/README.md index e741bc3..b191808 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ it is a Part of xDashboard on SaherElm IT Center which provides: -- all required Repositories, UnitOfWorks and other tools related to Data Manipulation in xApiGateway workspace. +- all requirements to Implement Data Access Layer in a **xDashboard** based Project. this module has following dependencies : @@ -13,7 +13,1205 @@ for configure and use this Module refer to DI.XDIHelperExtension.cs file. ## Implementation -for using this modules and its provided tools and services for preparing your data provider usages +### Supported Providers + +- MySQL +- SQLite +- Oracle +- SQLServer +- MongoDB + +### Extends Data Access + +#### Entiy + +an Entity usually maps a Table Row to Specified Class and Describe it's Columns as Class Properties and Specified Data types. + +Design Entities for a Business is Very Important Step. + +for using xDataService Features, you Hvae to Extends Entities from XBaseEntity. + +##### XBaseEntity + +all xDataService based Entities must inherit from this base Class. + +Id and Deleted Properties set to all TEntities. + +- Id: is Entity Primary Key; +- Deleted: used to Filter Soft Deleted Entities; + +```C# +public abstract XBaseEntity {} +``` + +using Key Type for Entity specified by Providing Generic Type which names TKey. + +there are some predefines common Key types in xDataService: + +- **XBaseIntIdEntity**; +- **XBaseLongIdEntity**; +- **XBaseGuidIdEntity**; +- **XBaseStringIdEntity**; + +so for Defining an Entity we can use them as Parent class. + +```C# +public class XTest : XBaseGuidIdEntity { + public string Firstname { get; set; } + public string Lastname { get; set; } +} +``` + +next thing which required for each Entity is a Class for Configuring Entity Properties. this class is used to Describe each property Mapping to Table Column. + +this is Optional step. but if you need, you have to extends your Entities Configurations from **XBaseEntityTypeConfiguration** class. + +```C# +public class XTestTypeConfiguration : XBaseEntityTypeConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { } +} +``` + +#### Context + +if your DataLayer required to use EF Core. +next step is to Create a DbContext. + +for using xDataService features you have to extends your Database Context from XDbContext. + +```C# +public class XSaherElmDbContext : XDbContext { + // + #region DbSets ... + public DbSet Tests { get; set; } + #endregion + + // + #region Navigation Property Entities ... + #endregion + + public XSaherElmDbContext( + DbContextOptions options, + XDataServiceConfiguration config + ) : base(options, config) { } + + // + public override void OnXModelCreating(ModelBuilder modelBuilder) + { } + + // + public override void OnXConfiguring(DbContextOptionsBuilder optionsBuilder) + { } +} +``` + +#### Repository Pattern + +by this feature you can manipulate specified Entity using Repository Pttern implementation. + +for each exists Entity, you need to Create a Repository: + +- **IXBaseRepository**: a base interface which defines all Commons Repository Patterns Actions. +- **XBaseEFRepository**: a base Implementation of Commons Repository Patterns Actions Specified Using EF Core. +- **XBaseMongoRepository**: a base Implementation of Commons Repository Patterns Actions for MongoDb. +- **XBaseInMemoryRepository**: a base Implementation of Commons Repository Patterns Actions for InMemory Data Stores. + +##### Key Generator + +if you need to using KeyGenerator Services based on your Entity you have to Create Custom Key Generator based on each Entity: + +- **IXKeyGenerator**: base interface for Providing Specified Key based Entities ID Generator. +- **XKeyGenerator**: base KeyGenerator Implementation abstraction. + +based on Default Implemented Key types for XBased Entity, there some Default KeyGenerator Implementations for usage: + +- XIntKeyGenerator +- XLongKeyGenerator +- XGuidKeyGenerator +- XStringKeyGenerator + +```C# +public interface IXTestKeyGenerator : IXKeyGenerator +{ } + +public class XTestKeyGenerator : XGuidKeyGenerator, IXTestKeyGenerator +{ } +``` + +##### Events + +events is a mechanism which notified when a Insert, Update or Delete action happens on Spacified Entity using Repository Implementation. + +if you need to using RepositoryEvents Services based on your Entity you have to Create Custom Repository Events based on each Entity: + +- **IXBaseRepositoryEvents**: a base interface for Describe several supported Repository Data Manipulation Events based on each Entity. +- **XBaseRepositoryEvents**: base Repository Event Implementation abstraction. + +```C# +public interface IXTestRepositoryEvents : IXBaseRepositoryEvents +{ } + +public class XTestRepositoryEvents : XBaseRepositoryEvents, IXTestRepositoryEvents +{ } +``` + +after preparing requirements you can define Repository Interface and Implementation based on selected Db Provider: + +```C# +// +// Global Interface for Repository Pattern based on XTest Entity ... +public interface IXTestRepository : IXBaseRepository +{ } + +// +// EF Repository Implementation based on XTest Entity ... +public class XTestEFRepository : XBaseEFRepository, IXTestRepository +{ + public XTestEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXTestKeyGenerator keyGenerator = null, + IXTestRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } +} + +// +// Mongo Db Repository Implementation based on XTest Entity ... +public class XTestMongoRepository : XBaseMongoRepository +{ + public XTestMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXTestKeyGenerator keyGenerator = null, + IXTestRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } +} + +// +// In Memory Store Repository Implementation based on XTest Entity ... +public class XTestInMemoryRepository : XBaseInMemoryRepository +{ + public XTestInMemoryRepository( + XDataServiceConfiguration configuration, + IXTestKeyGenerator keyGenerator = null, + IXTestRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } +} +``` + +as you can see in above Samples, the Repository Implementations has some Optional requirements which provides using Dependency Injections. + +Specially if Key Generator and RepositoryEvents provided, Repository Implementation using them, if not, use Default mechanism for Keys and Events not Supported. + +#### Data Seeding + +in xDataService usages, when defining Specified Repositories, you can Use **DbSeeder** for adding Default Items based on each Entity in Database on App's Startup. + +#### Seeding Mode + +- **None**: ignore Seeding Items. +- **AddIfNotExists**: add items if not Exists. +- **AddOrUpdate**: add or update items. + +for providing Seeding mechanism: + +- **IXBaseDbSeeder**: a base interface for Describe Seeder's Actions based on specified Entity. +- **XBaseDbSeeder**: a base Seeder Implementation based on specified Entity. + +if you need to Seed Items based on Each Entity of your Data Layer, you had to Create Seeder Services by following below: + +```C# +public interface IXTestDbSeeder : IXBaseDbSeeder +{ } + +public class XTestDbSeeder : XBaseDbSeeder, IXTestDbSeeder +{ + public XTestDbSeeder( + IXTestRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + database: "xApiDb", + entity: nameof(XTest), + repository: repository, + dataServiceConfiguration: dataServiceConfiguration + ) + { } +} +``` + +as you see Seeding only provided when using Repository Pattern. + +#### GraphQL Support + +on of most Important Features of xDataService module which Provided for usage, is the Ability of GraphQL usage of Entities. + +for this you have to follow Some Steps: + +##### GraphQL Entity Type + +first step is to Define GraphType based on each Entity: + +- **XBaseGraphObjectType**: base abstraction Implementation of Entity Graph Type. + +```C# +public class XTestGraphType : XBaseGraphObjectType +{ + public XTestGraphType() : base() + { + Field(x => x.Firstname); + Field(x => x.Lastname); + } +} +``` + +##### GraphQLTypeHelper + +each Entity Graph Type must have a GraphQLType Helper for Providing Requirement of GraphQL in Data Layer. + +- **IXBaseGraphQLTypeHelper**: a based interface for requirement. +- **XBaseGraphQLTypeHelper**: a based implementation for requirement. + +```C# +public interface IXTestGraphQLTypeHelper : IXBaseGraphQLTypeHelper +{ } + +public class XTestGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXTestGraphQLTypeHelper +{ + public XTestGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { + } + + public override string GetInQueryCollectionName() + { + return "tests"; + } + + public override string GetInQuerySingleName() + { + return "test"; + } +} +``` + +##### GraphQLQuery + +each Entity has Specified Queries for Support Data Manipulations and Supports them. + +all supported GraphQL actions Defines in this class. + +- **IXBaseGraphQLQuery**: a base interface for Describing some Defaults Repository Actions for GraphQL. +- **XBaseGraphQLQuery**: a base Implementation abstraction for providing some Defaults Repository Actions for GraphQL. + +for each Entity in your Data Layer you have to Define a GraphQL Query for Providing Query Actions. + +```C# +public class XTestGraphQuery : XBaseGraphQLQuery +{ + public XTestGraphQuery( + XDataServiceConfiguration configuration, + IXTestRepository repository, + IXTestGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } +} +``` + +##### GraphQLSchema + +a Graph Schema Provides all Requirements of a GraphQL endpoint. + +based on each Entity, you have to Provides a GraphQL endpoint using Schema. + +```C# +public class XTestGraphSchema : Schema +{ + public XTestGraphSchema(IServiceProvider services) : base(services) + { + Query = (XTestGraphQuery)services.GetService(typeof(XTestGraphQuery)); + } +} +``` + +### Configure xDataService + +all of Requirements for Data Layer must be Configured using appSettings and provided by IConfiguration to Modules. + +fo using xDataService you Have to Configure the Service in you appSettings.json file. + +this configurations mapped to **XDataServiceConfiguration** class. + +```C# +/// +/// Represent Configurations of DataService Module ... +/// +public partial class XDataServiceConfiguration +{ + /// + /// the base path for providing GraphQL ... + /// + /// + public string GraphQLBasePath { get; set; } = "/graphql"; + + /// + /// Data Bases Configuration ... + /// + /// + /// + public IDictionary Databases { get; set; } = new Dictionary(); + + /// + /// Enable Soft Delete Entities or Not ... + /// + /// + public bool EnableSoftDelete { get; set; } + + /// + /// Specified Seeding Mode ... + /// + public XDbSeedingMode SeedingMode { get; set; } = XDbSeedingMode.None; + + /// + /// Enable Tracking of Entities ... + /// Only Used on EFCore ... + /// + /// + public bool EnableTracking { get; set; } = false; + + /// + /// Enable Logging Details of Errors ... + /// Only Used on EFCore ... + /// + /// + public bool EnableDetailedErrors { get; set; } = false; + + /// + /// Enable Logging Sensitive Data ... + /// Only Used on EFCore ... + /// + /// + public bool EnableSensitiveDataLogging { get; set; } = false; + + /// + /// this is a way to provide Default Pagination Data on XQuery based requests ... + /// + /// + public PagingConfiguration PagingConfiguration { get; set; } = new PagingConfiguration(); +} + +/// +/// Describe a Database ... +/// +public class XDataBaseConfiguration +{ + /// + /// Provider Type ... + /// + /// + public XDbProviders Provider { get; set; } + + /// + /// Connection String which provide Requires Data to Connect to Db Provider ... + /// + /// + public string ConnectionString { get; set; } + + /// + /// Holds Seeding items ... + /// nameof(TEntity) => TEntity + /// + public IDictionary> SeedItems { get; set; } = new Dictionary>(); +} + +/// +/// this is a way to provide Default Pagination Data on XQuery based requests ... +/// +public partial class PagingConfiguration +{ + /// + /// Default Page Size ... + /// + /// + public int DefaultPageSize { get; set; } = XDataServiceConstants.DEFAULT_PAGE_SIZE; + + /// + /// restrict Maximum Page Size ... + /// + /// + public int MaxAvailablePageSize { get; set; } = XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE; + + /// + /// restrice Minimum Page Size ... + /// + /// + public int MinAvailablePageSize { get; set; } = XDataServiceConstants.MIN_AVAILABLE_PAGE_SIZE; +} +``` + +a sample Data Service Configuration must like this: + +```json +{ + ... + "DataServiceConfiguration": { + "Databases": { + "XSaherElmDb": { + "Provider": "SQLITE", + "ConnectionString": "Filename=./Db/XSaherElmDb.db", + "SeedItems": { + "XTest": [ + { + "Firstname": "Hadi", + "Lastname": "Khazaee Asl" + }, + { + "Firstname": "Amir Ali", + "Lastname": "Khazaee Asl" + } + ] + } + } + }, + "EnableTracking": true, + "EnableSoftDelete": false, + "EnableDetailedErrors": false, + "SeedingMode": "AddIfNotExists", + "EnableSensitiveDataLogging": true, + "PagingConfiguration": { + "DefaultPageSize": 20, + "MaxAvailablePageSize": 400, + "MinAvailablePageSize": 5 + }, + "GraphQLBasePath": "/graphs" + }, + ... +} +``` + +as you know the Configurations is a Multi Context implementations. +and also in Seed Items each Collection maps to Entity Names per DataBase. +and also Configurations provided based on each Database. + +next step after Configuring DataService is to Scribe Each Database by providing Type safe Definitions for Dependency Injection Container using following Provided Classes. + +```C# +/// +/// Data Base Descriptor ... +/// +public interface IXDatabaseDescriptor +{ + ... +} + +/// +/// Data Base Descriptor ... +/// +/// +public interface IXDatabaseDescriptor : IXDatabaseDescriptor +where TContext : XDbContext +{ + ... +} +``` + +```C# +/// +/// Data Base Descriptor ... +/// +public abstract class XDatabaseDescriptor : IXDatabaseDescriptor +{ + ... +} + +/// +/// Data Base Descriptor ... +/// +/// +public abstract class XDatabaseDescriptor : XDatabaseDescriptor, IXDatabaseDescriptor +where TContext : XDbContext +{ + ... +} +``` + +```C# +/// +/// a Global non Generic Repository Descriptor ... +/// +public class XRepositoryDescriptor +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +/// +/// +public class XRepositoryDescriptor : XRepositoryDescriptor +where TEntity : XBaseEntity +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation +> : XRepositoryDescriptor +where TEntity : XBaseEntity +where TKeyGeneratorService : IXKeyGenerator +where TKeyGeneratorImplementation : XKeyGenerator +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation +> +where TEntity : XBaseEntity +where TEventsService : IXBaseRepositoryEvents +where TKeyGeneratorService : IXKeyGenerator +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation +> +where TGraphSchema : Schema +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TEventsService : IXBaseRepositoryEvents +where TKeyGeneratorService : IXKeyGenerator +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TGraphQuery : XBaseGraphQLQuery +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XInMemoryRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> +where TGraphSchema : Schema +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TEventsService : IXBaseRepositoryEvents +where TKeyGeneratorService : IXKeyGenerator +where TRepositoryService : IXBaseRepository +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TRepositoryImplementation : XBaseInMemoryRepository +where TGraphQuery : XBaseGraphQLQuery +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XInMemoryRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TSeederService, + TSeederImplementation +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> +where TGraphSchema : Schema +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TSeederService : IXBaseDbSeeder +where TEventsService : IXBaseRepositoryEvents +where TKeyGeneratorService : IXKeyGenerator +where TRepositoryService : IXBaseRepository +where TSeederImplementation : XBaseDbSeeder +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TRepositoryImplementation : XBaseInMemoryRepository +where TGraphQuery : XBaseGraphQLQuery +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XMongoRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> +where TGraphSchema : Schema +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TEventsService : IXBaseRepositoryEvents +where TKeyGeneratorService : IXKeyGenerator +where TRepositoryService : IXBaseRepository +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TRepositoryImplementation : XBaseMongoRepository +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TGraphQuery : XBaseGraphQLQuery +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XMongoRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TSeederService, + TSeederImplementation +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> +where TGraphSchema : Schema +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TSeederService : IXBaseDbSeeder +where TEventsService : IXBaseRepositoryEvents +where TSeederImplementation : XBaseDbSeeder +where TKeyGeneratorService : IXKeyGenerator +where TRepositoryService : IXBaseRepository +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TRepositoryImplementation : XBaseMongoRepository +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TGraphQuery : XBaseGraphQLQuery +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XEFRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TContext +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> +where TGraphSchema : Schema +where TContext : XDbContext +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TEventsService : IXBaseRepositoryEvents +where TKeyGeneratorService : IXKeyGenerator +where TRepositoryService : IXBaseRepository +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TGraphQuery : XBaseGraphQLQuery +where TRepositoryImplementation : XBaseEFRepository +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} + +/// +/// a Repository Descriptor ... +/// +public class XEFRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TContext, + TSeederService, + TSeederImplementation +> : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema +> +where TGraphSchema : Schema +where TContext : XDbContext +where TGraphKey : IGraphType +where TEntity : XBaseEntity +where TGraph : XBaseGraphObjectType +where TSeederService : IXBaseDbSeeder +where TEventsService : IXBaseRepositoryEvents +where TSeederImplementation : XBaseDbSeeder +where TKeyGeneratorService : IXKeyGenerator +where TRepositoryService : IXBaseRepository +where TEventsImplementation : XBaseRepositoryEvents +where TKeyGeneratorImplementation : XKeyGenerator +where TGraphTypeHelperService : IXBaseGraphQLTypeHelper +where TGraphQuery : XBaseGraphQLQuery +where TRepositoryImplementation : XBaseEFRepository +where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper +{ + ... +} +``` + +based on using DbContext or not, you have to using above Descriptors for Describing your Databases. + +```C# +// +// Describe xSaherElmDatabase ... +// XSaherElmDbContext => a Db Context implementation which extends XDbContext ... +public class XSaherElmDatabaseDescriptor : XDatabaseDescriptor, IXDatabaseDescriptor +{ + public XSaherElmDatabaseDescriptor() : base( + // + // Name of Database which Configured in DataServiceConfigurations + // in App Settings json File ... + name: "XSaherElmDb", + // + // Describe all Exists Repositories ... + repositories: new List + { + // + // XTest Entity ... + // Since we Use EF Core, Create instance of + // XEFRepository ... + new XEFRepositoryDescriptor< + XTest, // Entity Type + Guid, // Entity Key Type + IXTestKeyGenerator, // Repository Key Generation Interface Type + XTestKeyGenerator, // Repository Key Generation Implementation Type + IXTestRepositoryEvents, // Repository Events Interface Type + XTestRepositoryEvents, // Repository Events Implementation Type + XTestGraphType, // Entity GraphQL Object Type Definition Type + GuidGraphType, // Entity GraphQL Key Object Type + XTestGraphQuery, // Entity GraphQL Query Type + IXTestGraphQLTypeHelper, // Entity GraphQL Type Helper Interface Type + XTestGraphQLTypeHelper, // Entity GraphQL Type Helper Implementation Type + XTestGraphSchema, // Entity GraphQL Schema Type + IXTestRepository, // Entity Repository Interface Type + XTestEFRepository, // Entity Repository Implementation Type + XSaherElmDbContext, // Entity DB Context Type + IXTestDbSeeder, // Entity Data Seeder Interface Type + XTestDbSeeder // Entity Data Seeder Implementation Type + > + {} + } + ) + { + // + // Options Builder for EF Core which used to Provides + // Migration Assembly to DataService ... + OptionsBuilder = b => b.MigrationsAssembly("xApi"); + } +} +``` + +as you can see, in above Descriptor we use all Implemented Classes and Interfaces. + +**Note**: there are Several Constructors of Repository Descriptors which you can use to Describe Several Types of Databases. + +### Register DataService + +after preparation of all requirements, final steps is Register DataService. + +there are two main step: + +- **DI Registration**: in this phase, all Database Descriptors used for Regitering all Services and Implementation of them. +- **Middleware Usage**: in this phase, all registere Repositories used for Seeding Data if Configured, and also Register GraphQL Supports if provided and Make available **/ui/playground** and **/ui/voyager** paths for GraphQL UI Supports when inside Development Environment. + +```C# +public class Startup +{ + // + private readonly XSaherElmDatabaseDescriptor saherelmDatabaseDescriptor; + + // + public Startup(IConfiguration configuration) + { + // + Configuration = configuration; + + // + // Instance Database Descriptors Here ... + saherelmDatabaseDescriptor = new XSaherElmDatabaseDescriptor(); + } + + // + public void ConfigureServices(IServiceCollection services) + { + ... + // + // Registration Database ... + // Each Database must Register in DI Container ... + services.AddXDatabase( + lifetime: lifeTime, + configuration: Configuration, + descriptor: saherelmDatabaseDescriptor + ); + ... + } + + // + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + ... + // + // Use Database Middleware ... + // Each Database must Used in Configuring Phase ... + app.UseXDatabase( + descriptor: saherelmDatabaseDescriptor, + isDevelopmentEnvironment: env.IsDevelopment() + ); + ... + } +} +``` + +### Repository Service + +a Repository Service is a Service based on XRepository Actions which Used Dto's instead of Entities. + +#### Dto + +a Dto (Data Transfer Object) is an instance of an Entity for Transfering across another Services for Business Logics. + +##### XBaseEntityDto + +all xDataService based Dtos must inherit from this base Class. + +Id and Deleted Properties set to all TEntities. + +- Id: is Entity Primary Key; +- Deleted: used to Filter Soft Deleted Entities; + +```C# +public abstract XBaseEntityDto {} +``` + +using Key Type for Entity specified by Providing Generic Type which names TKey. + +there are some predefines common Key types in xDataService: + +- **XBaseIntIDEntityDto**; +- **XBaseLongIDEntityDto**; +- **XBaseGuidIDEntityDto**; +- **XBaseStringIDEntityDto**; + +so for Defining an Entity we can use them as Parent class. + +```C# +public class XTestDto : XBaseEntityDto +{ + public string Firstname { get; set; } + public string Lastname { get; set; } +} +``` + +- **IXBaseRepositoryService**: base interface for Describe Repository Service Actions. +- **XBaseRepositoryService**: base Abstraction of Repository Service Implementation . + +foreach Repository, you have to inherit Repository Service from them: + +```C# +public interface IXTestRepositoryService : IXBaseRepositoryService +{ } + +public class XTestRepositoryService : XBaseRepositoryService, IXTestRepositoryService +{ + public XTestRepositoryService( + IXTestRepository repository, + IEnumerable mapperProfiles = null + ) : base(repository, mapperProfiles) + { } +} +``` + +final step is Registration in DI Container: + +```C# +... +service.AdScoped(); +... +``` + +### Data Controllers + +a Data Controller provides all Default Requirements Actions for Base Repository Patterns Data Manipulation Actions based on Restfull Architecture. + +there are two way to Provides Repositroy Actions through End Users: + +#### Repository Controller + +it usefull when you need to provides access to Specified Entity through Restfull Api. + +- **IXBaseRepositoryController**: a base interface for Describing a Repository based Controller Actions. +- **XBaseRepositoryController**: a base Abstraction of Implementation of a Repository based Controller Actioons. + +```C# +public partial class XTestRepositoryController : XBaseRepositoryController, IXBaseRepositoryController { + public XTestRepositoryController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXTestRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repository, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } +} +``` + +#### Service Controller + +it usefull when you need to provides access to Dto of Specified Entity through Restfull Api. + +- **IXBaseServiceController**: a base interface for Describing a Service based Controller Actions. +- **XBaseServiceController**: a base Abstraction of Implementation of a Service based Controller Actioons. + +```C# +public class XTestServiceController : XBaseServiceController, IXBaseServiceController { + public XTestServiceController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXTestRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repositoryService, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } +} +``` ## Maintainer