Initial Commit ...
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# DotNet ...
|
||||
bin
|
||||
obj
|
||||
|
||||
#
|
||||
# Natural Docs ...
|
||||
Documentation/*
|
||||
@@ -0,0 +1,82 @@
|
||||
using xDataService.Constants;
|
||||
|
||||
namespace xDataService.Configuration {
|
||||
/// <summary>
|
||||
/// Represent Configurations of DataService Module ...
|
||||
/// </summary>
|
||||
public partial class XDataServiceConfiguration {
|
||||
/// <summary>
|
||||
/// Provider Type ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public XDbProviders Provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable Soft Delete Entities or Not ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool EnableSoftDelete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection String which provide Requires Data to Connect to Db Provider ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable Tracking of Entities ...
|
||||
/// Only Used on EFCore ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool EnableTracking { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enable Logging Details of Errors ...
|
||||
/// Only Used on EFCore ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool EnableDetailedErrors { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enable Logging Sensitive Data ...
|
||||
/// Only Used on EFCore ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool EnableSensitiveDataLogging { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// this is a way to provide Default Pagination Data on XQuery based requests ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public PagingConfiguration PagingConfiguration { get; set; } = new PagingConfiguration ();
|
||||
|
||||
/// <summary>
|
||||
/// the base path for providing GraphQL ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string GraphQLBasePath { get; set; } = "/graphql";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// this is a way to provide Default Pagination Data on XQuery based requests ...
|
||||
/// </summary>
|
||||
public partial class PagingConfiguration {
|
||||
/// <summary>
|
||||
/// Default Page Size ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public int DefaultPageSize { get; set; } = XDataServiceConstants.DEFAULT_PAGE_SIZE;
|
||||
|
||||
/// <summary>
|
||||
/// restrict Maximum Page Size ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public int MaxAvailablePageSize { get; set; } = XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE;
|
||||
|
||||
/// <summary>
|
||||
/// restrice Minimum Page Size ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public int MinAvailablePageSize { get; set; } = XDataServiceConstants.MIN_AVAILABLE_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace xDataService.Configuration {
|
||||
public partial class XDbProviderConfigurations {
|
||||
/// <summary>
|
||||
/// Default ConnectionString Name ...
|
||||
/// </summary>
|
||||
public const string DEFAULT_CONNECTION_NAME = "DataConnection";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace xDataService.Constants {
|
||||
public partial struct ConfigurationNodeNames {
|
||||
public const string DB_PROVIDER_NODE = "XDBProvider";
|
||||
public const string DATA_SERVICE_NODE = "DataServiceConfiguration";
|
||||
public const string DATA_SERVICE_DB_SEED_NODE = "DbSeeder";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace xDataService.Constants {
|
||||
/// <summary>
|
||||
/// Default Pagination Values ...
|
||||
/// </summary>
|
||||
public partial struct XDataServiceConstants {
|
||||
public static int DEFAULT_PAGE_SIZE = 50;
|
||||
public static int MAX_AVAILABLE_PAGE_SIZE = 500;
|
||||
public static int MIN_AVAILABLE_PAGE_SIZE = 20;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace xDataService.Constants {
|
||||
/// <summary>
|
||||
/// Represent Supported DBMS for Managing Data ...
|
||||
/// </summary>
|
||||
public enum XDbProviders {
|
||||
None,
|
||||
MySQL,
|
||||
SQLite,
|
||||
SQLServer,
|
||||
MongoDB,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represent Supported DBMS for Managing Data ...
|
||||
/// </summary>
|
||||
public partial struct ProviderType {
|
||||
public const string MySQL = "MYSQL";
|
||||
public const string SQLite = "SQLITE";
|
||||
public const string SQLServer = "SQLSERVER";
|
||||
public const string MongoDB = "MONGODB";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 xModels.Interfaces;
|
||||
using xExceptions.Constants;
|
||||
using xModels.Base;
|
||||
using xModels.Dtos;
|
||||
using xDataService.Interfaces;
|
||||
|
||||
namespace xDataService.Controllers {
|
||||
public abstract class XBaseEntityController<TEntity, TKey> : XBaseController, IXEntityControllerActions<TEntity, TKey>
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
public readonly IXBaseRepository<TEntity, TKey> repository;
|
||||
|
||||
protected XBaseEntityController (
|
||||
ILogger logger,
|
||||
XAppConfiguration appConfiguration,
|
||||
XValidationProvider validationProvider,
|
||||
IXBaseRepository<TEntity, TKey> repository
|
||||
) : base (
|
||||
logger,
|
||||
appConfiguration,
|
||||
validationProvider
|
||||
) {
|
||||
//
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
//
|
||||
#region Interface Implementations ...
|
||||
//
|
||||
#region Retrieve ...
|
||||
[HttpGet ("{id}")]
|
||||
public virtual async Task<ActionResult<TEntity>> Get (
|
||||
[FromRoute] TKey id, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull (id);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.GetAsync (
|
||||
id,
|
||||
ignoreSoftDeleteds : ignoreSoftDeleteds,
|
||||
containsDetail : containsDetail
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public virtual async Task<ActionResult<IEnumerable<TEntity>>> GetAll (
|
||||
[FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.GetAllAsync (
|
||||
ignoreSoftDeleteds: ignoreSoftDeleteds,
|
||||
containsDetail: containsDetail
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet ("FindOne/{query}")]
|
||||
public virtual async Task<ActionResult<TEntity>> FindOne (
|
||||
[FromRoute] string query, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty (query);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.FindOneAsync (t =>
|
||||
t.PropValuesContains (query),
|
||||
ignoreSoftDeleteds : ignoreSoftDeleteds,
|
||||
containsDetail : containsDetail
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet ("FindMany/{query}")]
|
||||
public virtual async Task<ActionResult<IEnumerable<TEntity>>> FindMany (
|
||||
[FromRoute] string query, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotEmpty (query);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.FindManyAsync (t =>
|
||||
t.PropValuesContains (query),
|
||||
ignoreSoftDeleteds : ignoreSoftDeleteds,
|
||||
containsDetail : containsDetail
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet ("Query")]
|
||||
public virtual async Task<ActionResult<XQueryResult<TEntity>>> Query (
|
||||
[FromQuery] XQuery query, [FromQuery] bool ignoreSoftDeleteds = true
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull (query);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.QueryAsync (
|
||||
query,
|
||||
ignoreSoftDeleteds : ignoreSoftDeleteds
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Fix this ...
|
||||
// [HttpGet ("RequestPage")]
|
||||
// public virtual async Task<ActionResult<XPageResponse<TEntity>>> RequestPage (
|
||||
// [FromQuery] XPageRequest request, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false
|
||||
// ) {
|
||||
// //
|
||||
// try {
|
||||
// //
|
||||
// // Validate Args ...
|
||||
// ValidationProvider.NotNull (request);
|
||||
|
||||
// //
|
||||
// // Get Result ...
|
||||
// var result = await repository
|
||||
// .RequestPageAsync (
|
||||
// request,
|
||||
// ignoreSoftDeleteds : ignoreSoftDeleteds,
|
||||
// containsDetail : containsDetail
|
||||
// );
|
||||
|
||||
// //
|
||||
// return Ok (result
|
||||
// .ToDynamicObject ());
|
||||
// } catch (Exception ex) {
|
||||
// //
|
||||
// var result = GetExceptionActionResult (ex);
|
||||
// return result;
|
||||
// }
|
||||
// }
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Add ...
|
||||
[HttpPost]
|
||||
public virtual async Task<ActionResult<TEntity>> Add (
|
||||
[FromBody] TEntity item
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
if (!ModelState.IsValid) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
ValidationProvider.NotNull (item);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.AddAsync (
|
||||
item,
|
||||
saveChanges : true
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost ("AddOrUpdate")]
|
||||
public virtual async Task<ActionResult<TEntity>> AddOrUpdate (
|
||||
[FromBody] TEntity item
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
if (!ModelState.IsValid) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
ValidationProvider.NotNull (item);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.AddOrUpdateAsync (
|
||||
item,
|
||||
saveChanges : true
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost ("AddMany")]
|
||||
public virtual async Task<ActionResult> AddMany (
|
||||
[FromBody] XBaseRangeRequest<TEntity> request
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
if (!ModelState.IsValid) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder ()
|
||||
.AddNotNull (request)
|
||||
.AddNotZeroChilds (request.Items)
|
||||
.ValidateGroupAsync ();
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
await repository
|
||||
.AddRangeAsync (
|
||||
request.Items,
|
||||
saveChanges : true
|
||||
);
|
||||
|
||||
//
|
||||
return Ok ();
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Update ...
|
||||
[HttpPut ("{id}")]
|
||||
public virtual async Task<ActionResult<TEntity>> Update (
|
||||
[FromRoute] TKey id, [FromBody] TEntity item
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
if (!ModelState.IsValid) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder ()
|
||||
.AddNotNull (id, item)
|
||||
.ValidateGroupAsync ();
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.UpdateAsync (
|
||||
id,
|
||||
item,
|
||||
saveChanges : true
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost ("UpdateMany")]
|
||||
public virtual async Task<ActionResult<bool>> UpdateMany (
|
||||
[FromBody] XBaseRangeRequest<TEntity> request
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
if (!ModelState.IsValid) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder ()
|
||||
.AddNotNull (request)
|
||||
.AddNotZeroChilds (request.Items)
|
||||
.ValidateGroupAsync ();
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.UpdateRangeAsync (
|
||||
request.Items,
|
||||
saveChanges : true
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Exists ...
|
||||
[HttpGet ("{id}/IsExists")]
|
||||
public virtual async Task<ActionResult<bool>> IsExists (
|
||||
[FromRoute] TKey id, [FromQuery] bool ignoreSoftDeleteds = true
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull (id);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.IsExistsAsync (
|
||||
id,
|
||||
ignoreSoftDeleteds : ignoreSoftDeleteds
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Remove ...
|
||||
[HttpDelete ("{id}")]
|
||||
public virtual async Task<ActionResult<TEntity>> Remove (
|
||||
[FromRoute] TKey id,
|
||||
bool softDelete = true
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
ValidationProvider.NotNull (id);
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
var result = await repository
|
||||
.RemoveAsync (
|
||||
id,
|
||||
saveChanges : true,
|
||||
softDelete : softDelete
|
||||
);
|
||||
|
||||
//
|
||||
return Ok (result
|
||||
.ToDynamicObject ());
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost ("RemoveMany")]
|
||||
public virtual async Task<ActionResult> RemoveMany (
|
||||
[FromBody] XBaseRangeRequest<TEntity> request,
|
||||
bool softDelete = true
|
||||
) {
|
||||
//
|
||||
try {
|
||||
//
|
||||
// Validate Args ...
|
||||
await ValidationProvider
|
||||
.GroupValidationBuilder ()
|
||||
.AddNotNull (request)
|
||||
.AddNotZeroChilds (request.Items)
|
||||
.ValidateGroupAsync ();
|
||||
|
||||
//
|
||||
// Get Result ...
|
||||
await repository
|
||||
.RemoveRangeAsync (
|
||||
request.Items,
|
||||
saveChanges : true,
|
||||
softDelete : softDelete
|
||||
);
|
||||
|
||||
//
|
||||
return Ok ();
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
var result = GetExceptionActionResult (ex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GraphQL.Server;
|
||||
using GraphQL.Server.Ui.Playground;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Conventions;
|
||||
using MySql.EntityFrameworkCore.Extensions;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Configuration;
|
||||
using xDataService.Constants;
|
||||
using xDataService.Db;
|
||||
using xDataService.Extensions;
|
||||
using xDataService.Interfaces;
|
||||
using xExceptions.Constants;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.DI {
|
||||
public static class XDIHelperExtension {
|
||||
/// <summary>
|
||||
/// Extract Connection String From IConfiguration
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="connectionName"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetXConnectionString (
|
||||
this IConfiguration config,
|
||||
string connectionName = null
|
||||
) {
|
||||
//
|
||||
if (connectionName.IsNullOrEmpty ()) {
|
||||
connectionName = XDbProviderConfigurations.DEFAULT_CONNECTION_NAME;
|
||||
}
|
||||
|
||||
//
|
||||
return config.GetConnectionString (connectionName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Data Provider Type from Configurations
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static XDbProviders GetXDbProviderType (this IConfiguration source) {
|
||||
//
|
||||
var provider = (source[$"{ConfigurationNodeNames.DB_PROVIDER_NODE}"])
|
||||
.ToNormalString ();
|
||||
|
||||
//
|
||||
return provider.ToDbProvider ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrive XDataService Configurations
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="connectionName"></param>
|
||||
/// <returns></returns>
|
||||
public static XDataServiceConfiguration GetXDataServiceConfiguration (
|
||||
this IConfiguration source,
|
||||
string connectionName = null
|
||||
) {
|
||||
//
|
||||
var xDataServiceConfigSection = source
|
||||
.GetSection (ConfigurationNodeNames.DATA_SERVICE_NODE);
|
||||
var result = xDataServiceConfigSection.Get<XDataServiceConfiguration> ();
|
||||
|
||||
//
|
||||
result.Provider = source.GetXDbProviderType ();
|
||||
result.ConnectionString = source.GetXConnectionString (connectionName);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register XDataService Configuration
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="connectionName"></param>
|
||||
public static void AddXDataServiceConfiguration (
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration,
|
||||
string connectionName = null
|
||||
) {
|
||||
//
|
||||
var dataServiceConfiguration = configuration
|
||||
.GetXDataServiceConfiguration (connectionName);
|
||||
if (dataServiceConfiguration.IsNull ()) {
|
||||
dataServiceConfiguration = new XDataServiceConfiguration ();
|
||||
}
|
||||
|
||||
//
|
||||
services.AddSingleton<XDataServiceConfiguration> (dataServiceConfiguration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register XDataService Configuration
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="configuration"></param>
|
||||
public static void AddXDataServiceConfiguration (
|
||||
this IServiceCollection services,
|
||||
XDataServiceConfiguration configuration
|
||||
) {
|
||||
//
|
||||
if (configuration.IsNull ()) {
|
||||
configuration = new XDataServiceConfiguration ();
|
||||
}
|
||||
|
||||
//
|
||||
services.AddSingleton<XDataServiceConfiguration> (configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register XDataService on DI
|
||||
/// Only Used when EFCore Provider configured to use ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="connectionName"></param>
|
||||
/// <param name="optionsBuilder"></param>
|
||||
public static void AddXDataService<TDbContext, TDbSeeder> (
|
||||
this IServiceCollection services,
|
||||
IConfiguration config,
|
||||
ServiceLifetime lifetime,
|
||||
string connectionName = null,
|
||||
Action<dynamic> optionsBuilder = null
|
||||
)
|
||||
where TDbContext : XDbContext
|
||||
where TDbSeeder : IXDbSeeder {
|
||||
//
|
||||
// Register DataService Configuration ...
|
||||
if (services.GetRegisteredService<XDataServiceConfiguration> ().IsNull ()) {
|
||||
Console.WriteLine ($"XDataService: there is no provided DataService Config, try to register default ...");
|
||||
services.AddXDataServiceConfiguration (config, connectionName);
|
||||
}
|
||||
|
||||
//
|
||||
// prevent from going forward if there is no Provider configured ...
|
||||
var providerType = config.GetXDbProviderType ();
|
||||
Console.WriteLine ($"XDataService: registered db provider type is => {providerType} ...");
|
||||
if (providerType == XDbProviders.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve IXDataServiceHelper ...
|
||||
var dataServiceHelper = services.GetRegisteredService<IXDataServiceHelper> ();
|
||||
if (!dataServiceHelper.IsNull ()) {
|
||||
//
|
||||
// Use Db Context Helper ...
|
||||
Console.WriteLine ($"XDataService: use provided IXDbContextHelper ...");
|
||||
|
||||
//
|
||||
// Retrieve Connection String ...
|
||||
var addContext = true;
|
||||
var connectionString = config.GetXConnectionString (connectionName);
|
||||
switch (providerType) {
|
||||
//
|
||||
case XDbProviders.MySQL:
|
||||
addContext = true;
|
||||
services.AddEntityFrameworkMySQL ();
|
||||
break;
|
||||
|
||||
//
|
||||
case XDbProviders.SQLite:
|
||||
addContext = true;
|
||||
services.AddEntityFrameworkSqlite ();
|
||||
break;
|
||||
|
||||
//
|
||||
case XDbProviders.SQLServer:
|
||||
addContext = true;
|
||||
services.AddEntityFrameworkSqlServer ();
|
||||
break;
|
||||
|
||||
//
|
||||
case XDbProviders.MongoDB:
|
||||
addContext = false;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Register DbContext in DI ...
|
||||
if (addContext) {
|
||||
dataServiceHelper.AddDbContext (
|
||||
services,
|
||||
connectionString,
|
||||
providerType,
|
||||
lifetime,
|
||||
optionsBuilder
|
||||
);
|
||||
|
||||
//
|
||||
// Register Unit Of Works ...
|
||||
services.Add (new ServiceDescriptor (typeof (IXUnitOfWorks<TDbContext>), typeof (XUnitOfWorks<TDbContext>), lifetime));
|
||||
}
|
||||
|
||||
//
|
||||
// Comment this in related to Task no. 27 ...
|
||||
// Register IXSequentialGuid for handle XBaseGuidEntities ...
|
||||
// services.AddSingleton<IXSequentialGuid, XSequentialGuid> ();
|
||||
|
||||
//
|
||||
// Register All Repository Patterns ...
|
||||
AddRepositories<TDbContext, TDbSeeder> (services, config, lifetime, providerType);
|
||||
} else {
|
||||
Console.WriteLine ($"XDataService: there is no provided IXDbContextHelper, data service registration failed ...");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register XDataService on DI
|
||||
/// Only Used when non EFCore Provider configured to use ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="connectionName"></param>
|
||||
public static void AddXDataService<TDbSeeder> (
|
||||
this IServiceCollection services,
|
||||
IConfiguration config,
|
||||
ServiceLifetime lifetime,
|
||||
string connectionName = null
|
||||
)
|
||||
where TDbSeeder : IXDbSeeder {
|
||||
//
|
||||
// Register DataService Configuration ...
|
||||
if (services.GetRegisteredService<XDataServiceConfiguration> ().IsNull ()) {
|
||||
Console.WriteLine ($"XDataService: there is no provided DataService Config, try to register default ...");
|
||||
services.AddXDataServiceConfiguration (config, connectionName);
|
||||
}
|
||||
|
||||
//
|
||||
// prevent from going forward if there is no Provider configured ...
|
||||
var providerType = config.GetXDbProviderType ();
|
||||
Console.WriteLine ($"XDataService: registered db provider type is => {providerType} ...");
|
||||
if (providerType == XDbProviders.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve IXDataServiceHelper ...
|
||||
var dataServiceHelper = services.GetRegisteredService<IXDataServiceHelper> ();
|
||||
if (!dataServiceHelper.IsNull ()) {
|
||||
//
|
||||
// Use Db Context Helper ...
|
||||
Console.WriteLine ($"XDataService: use provided IXDataServiceHelper ...");
|
||||
|
||||
//
|
||||
// Retrieve Connection String ...
|
||||
var connectionString = config.GetXConnectionString (connectionName);
|
||||
switch (providerType) {
|
||||
//
|
||||
case XDbProviders.MySQL:
|
||||
case XDbProviders.SQLite:
|
||||
case XDbProviders.SQLServer:
|
||||
Console.WriteLine ($"XDataService: for EF Provider Types use AddXDataService<TDbContext, TDbSeeder> instead of AddXDataService<TDbSeeder> ...");
|
||||
XException.InvalidConfiguration.Throw ();
|
||||
break;
|
||||
|
||||
//
|
||||
case XDbProviders.MongoDB:
|
||||
//
|
||||
#region XBaseEntity Mappers ...
|
||||
//
|
||||
// Map ID as BsonId here ...
|
||||
BsonClassMap.RegisterClassMap<XBaseEntity<Guid>> (cm => {
|
||||
//
|
||||
cm.AutoMap ();
|
||||
cm.MapIdMember (c => c.Id);
|
||||
});
|
||||
BsonClassMap.RegisterClassMap<XBaseEntity<int>> (cm => {
|
||||
//
|
||||
cm.AutoMap ();
|
||||
cm.MapIdMember (c => c.Id);
|
||||
});
|
||||
BsonClassMap.RegisterClassMap<XBaseEntity<string>> (cm => {
|
||||
//
|
||||
cm.AutoMap ();
|
||||
cm.MapIdMember (c => c.Id);
|
||||
});
|
||||
#endregion
|
||||
|
||||
//
|
||||
var conventionPacks = dataServiceHelper.MongoConventionPacks ();
|
||||
if (!conventionPacks.IsNull ()) {
|
||||
ConventionRegistry.Register (
|
||||
nameof (
|
||||
dataServiceHelper.MongoConventionPacks
|
||||
),
|
||||
conventionPacks,
|
||||
type => !type.FullName
|
||||
.IsNullOrEmpty ()
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
dataServiceHelper.RegisterMongoExtras ();
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Comment this in related to Task no. 27 ...
|
||||
// Register IXSequentialGuid for handle XBaseGuidEntities ...
|
||||
// services.AddSingleton<IXSequentialGuid, XSequentialGuid> ();
|
||||
|
||||
//
|
||||
// Register All Repository Patterns ...
|
||||
AddRepositories<TDbSeeder> (services, config, lifetime, providerType);
|
||||
} else {
|
||||
Console.WriteLine ($"XDataService: there is no provided IXDbContextHelper, data service registration failed ...");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register XGraphQL service ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="options"></param>
|
||||
public static void AddXGraphQL (
|
||||
this IServiceCollection services,
|
||||
Action<GraphQLOptions> options = null
|
||||
) {
|
||||
//
|
||||
var xGraphQLHelper = services.GetRegisteredService<IXGraphQLHelper> ();
|
||||
if (!xGraphQLHelper.IsNull ()) {
|
||||
//
|
||||
xGraphQLHelper.AddXGraphEnumTypes (services);
|
||||
xGraphQLHelper.AddXGraphObjectTypes (services);
|
||||
xGraphQLHelper.AddXGraphInputTypes (services);
|
||||
xGraphQLHelper.AddXGraphQueries (services);
|
||||
xGraphQLHelper.AddXGraphMutations (services);
|
||||
xGraphQLHelper.AddXGraphSubscriptions (services);
|
||||
xGraphQLHelper.AddXGraphSchemas (services);
|
||||
|
||||
//
|
||||
// Add GraphQL Server ...
|
||||
if (options.IsNull ()) {
|
||||
//
|
||||
options = x => { };
|
||||
Console.WriteLine ($"XDataService: there is no provided GraphQLOption, use default ...");
|
||||
}
|
||||
|
||||
//
|
||||
var xGraphQlBuilder = services.AddGraphQL (options)
|
||||
.AddNewtonsoftJson (deserializerSettings => { }, serializerSettings => { })
|
||||
.AddWebSockets ()
|
||||
.AddDataLoader ()
|
||||
.AddGraphTypes ();
|
||||
|
||||
//
|
||||
xGraphQLHelper.AddXGraphTypes (xGraphQlBuilder);
|
||||
} else {
|
||||
Console.WriteLine ($"XDataService: there is no provided IXGraphQLHelper, data service registration failed ...");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use XDataService MiddleWare
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
public static void UseXDataService (
|
||||
this IApplicationBuilder app
|
||||
) {
|
||||
app.SeedData ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use XGraphQL Middleware ...
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="withPlayground"></param>
|
||||
public static void UseXGraphQL (
|
||||
this IApplicationBuilder app,
|
||||
GraphQLPlaygroundOptions options = null,
|
||||
bool withPlayground = true
|
||||
) {
|
||||
//
|
||||
// Use GraphQl WebSockets ...
|
||||
app.UseWebSockets ();
|
||||
|
||||
//
|
||||
// Registered Graph Types ...
|
||||
using (var scope = app.ApplicationServices.CreateScope ()) {
|
||||
//
|
||||
var xGraphQLHelper = scope.ServiceProvider.GetService<IXGraphQLHelper> ();
|
||||
if (!xGraphQLHelper.IsNull ()) {
|
||||
xGraphQLHelper.UseXGraph (app);
|
||||
} else {
|
||||
Console.WriteLine ($"XDataService: there is no provided IXGraphQLHelper, data service GraphQL not using ...");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Use GraphQL Playground UI ...
|
||||
if (withPlayground) {
|
||||
app.UseGraphQLPlayground (options);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
#region Private ...
|
||||
/// <summary>
|
||||
/// Register Repositories on DI as Services
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="provider"></param>
|
||||
private static void AddRepositories<TDbContext, TDbSeeder> (
|
||||
IServiceCollection services,
|
||||
IConfiguration config,
|
||||
ServiceLifetime lifetime,
|
||||
XDbProviders provider
|
||||
)
|
||||
where TDbContext : XDbContext
|
||||
where TDbSeeder : IXDbSeeder {
|
||||
//
|
||||
// Add Repositories Based On Provider Here ...
|
||||
switch (provider) {
|
||||
case XDbProviders.MySQL:
|
||||
case XDbProviders.SQLite:
|
||||
case XDbProviders.SQLServer:
|
||||
AddEFRepositories<TDbContext> (services, lifetime);
|
||||
break;
|
||||
case XDbProviders.MongoDB:
|
||||
Console.WriteLine ($"XDataService: for Mongo Provider Types use AddXDataService<TDbSeeder> instead of AddXDataService<TDbContext, TDbSeeder> ...");
|
||||
XException.InvalidConfiguration.Throw ();
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve IXDataServiceHelper ...
|
||||
var dataServiceHelper = services.GetRegisteredService<IXDataServiceHelper> ();
|
||||
if (dataServiceHelper.IsNull ()) {
|
||||
//
|
||||
Console.WriteLine ($"XDataService: there is no provided IXDataServiceHelper, db seeder registration failed ...");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Add DbSeeder Configuration ...
|
||||
dataServiceHelper.AddDbSeederConfiguration (services, config);
|
||||
|
||||
//
|
||||
// Add Db Seeder ...
|
||||
dataServiceHelper.AddDbSeeder<TDbSeeder> (services, config, lifetime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register Repositories on DI as Services
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="provider"></param>
|
||||
private static void AddRepositories<TDbSeeder> (
|
||||
IServiceCollection services,
|
||||
IConfiguration config,
|
||||
ServiceLifetime lifetime,
|
||||
XDbProviders provider
|
||||
)
|
||||
where TDbSeeder : IXDbSeeder {
|
||||
//
|
||||
// Add Repositories Based On Provider Here ...
|
||||
switch (provider) {
|
||||
//
|
||||
case XDbProviders.MySQL:
|
||||
case XDbProviders.SQLite:
|
||||
case XDbProviders.SQLServer:
|
||||
Console.WriteLine ($"XDataService: for EF Provider Types use AddXDataService<TDbContext, TDbSeeder> instead of AddXDataService<TDbSeeder> ...");
|
||||
XException.InvalidConfiguration.Throw ();
|
||||
break;
|
||||
|
||||
//
|
||||
case XDbProviders.MongoDB:
|
||||
AddMongoRepositories (services, lifetime);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve IXDataServiceHelper ...
|
||||
var dataServiceHelper = services.GetRegisteredService<IXDataServiceHelper> ();
|
||||
if (dataServiceHelper.IsNull ()) {
|
||||
//
|
||||
Console.WriteLine ($"XDataService: there is no provided IXDataServiceHelper, db seeder registration failed ...");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Add DbSeeder Configuration ...
|
||||
dataServiceHelper.AddDbSeederConfiguration (services, config);
|
||||
|
||||
//
|
||||
// Add Db Seeder ...
|
||||
dataServiceHelper.AddDbSeeder<TDbSeeder> (services, config, lifetime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Entity Framework Repositories
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
private static void AddEFRepositories<TDbContext> (
|
||||
this IServiceCollection services,
|
||||
ServiceLifetime lifetime
|
||||
)
|
||||
where TDbContext : XDbContext {
|
||||
//
|
||||
var repoServices = new List<ServiceDescriptor> ();
|
||||
|
||||
//
|
||||
var dataServiceConfig = services.GetRegisteredService<XDataServiceConfiguration> ();
|
||||
if (!dataServiceConfig.IsNull ()) {
|
||||
//
|
||||
// Register Base Repositories if Required ...
|
||||
}
|
||||
|
||||
//
|
||||
// Adding Services to DI ...
|
||||
if (repoServices.Count > 0) {
|
||||
repoServices
|
||||
.ToList ()
|
||||
.ForEach (s => services.Add (s));
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve IXDataServiceHelper ...
|
||||
var dataServiceHelper = services.GetRegisteredService<IXDataServiceHelper> ();
|
||||
if (!dataServiceHelper.IsNull ()) {
|
||||
//
|
||||
// Register Repositories ...
|
||||
dataServiceHelper.AddRepositories (services, lifetime);
|
||||
|
||||
//
|
||||
// Register KeyGenerators ...
|
||||
dataServiceHelper.AddKeyGenerators (services);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add MongoDb Repositories
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
private static void AddMongoRepositories (
|
||||
this IServiceCollection services,
|
||||
ServiceLifetime lifetime
|
||||
) {
|
||||
//
|
||||
var repoServices = new List<ServiceDescriptor> ();
|
||||
|
||||
//
|
||||
var dataServiceConfig = services.GetRegisteredService<XDataServiceConfiguration> ();
|
||||
if (!dataServiceConfig.IsNull ()) {
|
||||
//
|
||||
// Register Base Repositories if Required ...
|
||||
}
|
||||
|
||||
//
|
||||
// Adding Services to DI ...
|
||||
if (repoServices.Count > 0) {
|
||||
repoServices
|
||||
.ToList ()
|
||||
.ForEach (s => services.Add (s));
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve IXDataServiceHelper ...
|
||||
var dataServiceHelper = services.GetRegisteredService<IXDataServiceHelper> ();
|
||||
if (!dataServiceHelper.IsNull ()) {
|
||||
//
|
||||
// Register Repositories ...
|
||||
dataServiceHelper.AddRepositories (services, lifetime);
|
||||
|
||||
//
|
||||
// Register KeyGenerators ...
|
||||
dataServiceHelper.AddKeyGenerators (services);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do Seeding Initialization Data on DbContext
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
private static void SeedData (
|
||||
this IApplicationBuilder app
|
||||
) {
|
||||
//
|
||||
using (var scope = app.ApplicationServices.CreateScope ()) {
|
||||
//
|
||||
var dbSeeder = scope.ServiceProvider.GetService<IXDbSeeder> ();
|
||||
Console.WriteLine ($"XDataService: dbSeeder retrieved from DI is => {!dbSeeder.IsNull()}");
|
||||
|
||||
//
|
||||
if (!dbSeeder.IsNull ()) {
|
||||
try {
|
||||
//
|
||||
dbSeeder.Seed ()
|
||||
.GetAwaiter ()
|
||||
.GetResult ();
|
||||
} catch (Exception ex) {
|
||||
//
|
||||
Console.WriteLine (" ");
|
||||
Console.WriteLine ($"XDataService: Seeding Error: {ex.Message}");
|
||||
XException.InvalidConfiguration.Throw ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xDataService.Configuration;
|
||||
|
||||
namespace xDataService.Db {
|
||||
/// <summary>
|
||||
/// Provide an abstraction layout arround DBContext ...
|
||||
/// Only Used on EFCore ...
|
||||
/// </summary>
|
||||
public abstract class XDbContext : DbContext {
|
||||
private readonly XDataServiceConfiguration config;
|
||||
|
||||
//
|
||||
#region Entities ...
|
||||
//
|
||||
// Register Default and Base Entities ...
|
||||
#endregion
|
||||
|
||||
//
|
||||
// Constructor ...
|
||||
public XDbContext (
|
||||
DbContextOptions options,
|
||||
XDataServiceConfiguration config
|
||||
) : base (options) {
|
||||
//
|
||||
try {
|
||||
Database.EnsureCreated ();
|
||||
Database.Migrate ();
|
||||
} catch { }
|
||||
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare Fluent API ...
|
||||
protected override void OnModelCreating (ModelBuilder modelBuilder) {
|
||||
//
|
||||
OnXModelCreating (modelBuilder);
|
||||
|
||||
//
|
||||
// Since this class inherit from Identity Db Context,
|
||||
// we have to pass this to Building Identity Models ...
|
||||
base.OnModelCreating (modelBuilder);
|
||||
}
|
||||
|
||||
//
|
||||
public abstract void OnXModelCreating (ModelBuilder modelBuilder);
|
||||
|
||||
//
|
||||
// Prepare Configuring ...
|
||||
protected override void OnConfiguring (DbContextOptionsBuilder optionsBuilder) {
|
||||
//
|
||||
if (config.EnableDetailedErrors) {
|
||||
optionsBuilder.EnableDetailedErrors ();
|
||||
}
|
||||
|
||||
//
|
||||
if (config.EnableSensitiveDataLogging) {
|
||||
optionsBuilder.EnableSensitiveDataLogging ();
|
||||
}
|
||||
|
||||
//
|
||||
if (config.EnableTracking) {
|
||||
optionsBuilder.UseQueryTrackingBehavior (QueryTrackingBehavior.TrackAll);
|
||||
} else {
|
||||
optionsBuilder.UseQueryTrackingBehavior (QueryTrackingBehavior.NoTracking);
|
||||
}
|
||||
|
||||
//
|
||||
OnXConfiguring (optionsBuilder);
|
||||
|
||||
//
|
||||
base.OnConfiguring (optionsBuilder);
|
||||
}
|
||||
|
||||
//
|
||||
public abstract void OnXConfiguring (DbContextOptionsBuilder optionsBuilder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xDataService.Interfaces;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Db {
|
||||
/// <summary>
|
||||
/// Provide Unit Of Works Design Pattern for Db Transactions ...
|
||||
/// Only Used on EFCore ...
|
||||
/// </summary>
|
||||
/// <typeparam name="TDbContext"></typeparam>
|
||||
public class XUnitOfWorks<TDbContext> : IXUnitOfWorks<TDbContext>
|
||||
where TDbContext : XDbContext {
|
||||
public TDbContext DbContext { get; }
|
||||
|
||||
public XUnitOfWorks (TDbContext context) {
|
||||
this.DbContext = context;
|
||||
}
|
||||
|
||||
public DbSet<T> GetDbSet<T, TKey> ()
|
||||
where T : XBaseEntity<TKey> {
|
||||
return DbContext.Set<T> ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Changes
|
||||
/// </summary>
|
||||
public int SaveChanges () {
|
||||
return DbContext.SaveChanges ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Changes Async
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<int> SaveChangesAsync () {
|
||||
return await DbContext.SaveChangesAsync ();
|
||||
}
|
||||
|
||||
public void Dispose () {
|
||||
DbContext.Dispose ();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using xDataService.Interfaces;
|
||||
using xDataService.Models;
|
||||
|
||||
namespace xDataService.Events {
|
||||
/// <summary>
|
||||
/// Some Events can be Raised on Some Actions Happens on Repositories ...
|
||||
/// this is the Base Events Class ...
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class XBaseRepositoryEvents<T> : IXBaseRepositoryEvents<T> {
|
||||
//
|
||||
private readonly ISubject<XBaseEventModel<T>> onAddSubject;
|
||||
private readonly ISubject<XBaseEventModel<T>> onUpdateSubject;
|
||||
private readonly ISubject<XBaseEventModel<T>> onRemoveSubject;
|
||||
|
||||
//
|
||||
private readonly ISubject<XBaseEventModel<IEnumerable<T>>> onAddManySubject;
|
||||
private readonly ISubject<XBaseEventModel<IEnumerable<T>>> onUpdateManySubject;
|
||||
private readonly ISubject<XBaseEventModel<IEnumerable<T>>> onRemoveManySubject;
|
||||
|
||||
//
|
||||
protected XBaseRepositoryEvents () {
|
||||
//
|
||||
onAddSubject = new ReplaySubject<XBaseEventModel<T>> (1);
|
||||
onUpdateSubject = new ReplaySubject<XBaseEventModel<T>> (1);
|
||||
onRemoveSubject = new ReplaySubject<XBaseEventModel<T>> (1);
|
||||
|
||||
//
|
||||
onAddManySubject = new ReplaySubject<XBaseEventModel<IEnumerable<T>>> (1);
|
||||
onUpdateManySubject = new ReplaySubject<XBaseEventModel<IEnumerable<T>>> (1);
|
||||
onRemoveManySubject = new ReplaySubject<XBaseEventModel<IEnumerable<T>>> (1);
|
||||
}
|
||||
|
||||
//
|
||||
public void AddEvent (XBaseEventModel<T> model) => onAddSubject.OnNext (model);
|
||||
|
||||
public void UpdateEvent (XBaseEventModel<T> model) => onUpdateSubject.OnNext (model);
|
||||
|
||||
public void RemoveEvent (XBaseEventModel<T> model) => onRemoveSubject.OnNext (model);
|
||||
|
||||
//
|
||||
public void AddManyEvent (XBaseEventModel<IEnumerable<T>> model) => onAddManySubject.OnNext (model);
|
||||
public void UpdateManyEvent (XBaseEventModel<IEnumerable<T>> model) => onUpdateManySubject.OnNext (model);
|
||||
public void RemoveManyEvent (XBaseEventModel<IEnumerable<T>> model) => onRemoveManySubject.OnNext (model);
|
||||
|
||||
//
|
||||
public IObservable<XBaseEventModel<T>> OnAddObservable => onAddSubject.AsObservable ();
|
||||
|
||||
public IObservable<XBaseEventModel<T>> OnUpdateObservable => onUpdateSubject.AsObservable ();
|
||||
|
||||
public IObservable<XBaseEventModel<T>> OnRemoveObservable => onRemoveSubject.AsObservable ();
|
||||
|
||||
//
|
||||
public IObservable<XBaseEventModel<IEnumerable<T>>> OnAddManyObservable => onAddManySubject.AsObservable ();
|
||||
public IObservable<XBaseEventModel<IEnumerable<T>>> OnUpdateManyObservable => onUpdateManySubject.AsObservable ();
|
||||
public IObservable<XBaseEventModel<IEnumerable<T>>> OnRemoveManyObservable => onRemoveManySubject.AsObservable ();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using MySql.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace xDataService.Extensions {
|
||||
public static class DbContextOptionExtensions {
|
||||
/// <summary>
|
||||
/// convert dynamic object to MySqlDbContextOptionBuilder ...
|
||||
/// Only Used in EFCore ...
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static Action<MySQLDbContextOptionsBuilder> ToMySQLDbContextOptionsBuilder (
|
||||
this Action<dynamic> source
|
||||
) {
|
||||
return ((Action<MySQLDbContextOptionsBuilder>) source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// convert dynamic object to SqliteDbContextOptionBuilder ...
|
||||
/// Only Used in EFCore ...
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static Action<SqliteDbContextOptionsBuilder> ToSqliteDbContextOptionsBuilder (
|
||||
this Action<dynamic> source
|
||||
) {
|
||||
return ((Action<SqliteDbContextOptionsBuilder>) source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// convert dynamic object to SqlServerDbContextOptionBuilder ...
|
||||
/// Only Used in EFCore ...
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static Action<SqlServerDbContextOptionsBuilder> ToSqlServerDbContextOptionsBuilder (
|
||||
this Action<dynamic> source
|
||||
) {
|
||||
return ((Action<SqlServerDbContextOptionsBuilder>) source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using xCommons.Extensions;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Extensions {
|
||||
public static class EntityExtensions {
|
||||
/// <summary>
|
||||
/// return all values of an entity properties as a json string ...
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static string GetPropValues<T> (this T item)
|
||||
where T : XBaseEntity<T> {
|
||||
//
|
||||
var props = item.GetType ().GetProperties ();
|
||||
var vals = props.Select (p => p.GetValue (item));
|
||||
|
||||
//
|
||||
return vals.ToJSON ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check values of all properties of an Entity contains specific value or not ...
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static bool PropValuesContains<T> (this T item, string value)
|
||||
where T : XBaseEntity<T> => item.GetPropValues ()
|
||||
.ToNormalString ()
|
||||
.Contains (value);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Default Column Map of specific Entity ...
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IDictionary<string, Expression<Func<T, object>>> GetDefaultColumnsMap<T> (this T item)
|
||||
where T : XBaseEntity<T> {
|
||||
//
|
||||
if (item.IsNull ()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var result = new Dictionary<string, Expression<Func<T, object>>> ();
|
||||
var props = item.GetType ().GetProperties ();
|
||||
|
||||
//
|
||||
foreach (var prop in props) {
|
||||
result.Add (
|
||||
prop.Name,
|
||||
t => t.GetType ().GetProperty (prop.Name).GetValue (t, null));
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Constants;
|
||||
using xExceptions.Constants;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Extensions {
|
||||
public static class IQueryableExtensions {
|
||||
/// <summary>
|
||||
/// Apply Search Filter on a Query
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IQueryable<T> ApplyFilter<T, TKey> (
|
||||
this IQueryable<T> source,
|
||||
string filter
|
||||
)
|
||||
where T : XBaseEntity<TKey> {
|
||||
//
|
||||
// Apply Filter ...
|
||||
return source.Where (r => r.PropValuesContains (filter));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Search Filter on a Query
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static async Task<IQueryable<T>> ApplyFilterAsync<T, TKey> (
|
||||
this IQueryable<T> source,
|
||||
string filter
|
||||
)
|
||||
where T : XBaseEntity<TKey> {
|
||||
//
|
||||
// Where Clause ...
|
||||
Expression<Func<T, bool>> whereClause = r => r
|
||||
.PropValuesContains (filter);
|
||||
|
||||
//
|
||||
// Generate Where Function ...
|
||||
var whereFunc = whereClause.Compile ();
|
||||
|
||||
//
|
||||
// Get Enumerable ...
|
||||
var enumerator = source
|
||||
.AsAsyncEnumerable ();
|
||||
|
||||
//
|
||||
var result = new List<T> ();
|
||||
await
|
||||
foreach (var entity in enumerator) {
|
||||
//
|
||||
var isApproved = whereFunc (entity);
|
||||
if (isApproved) {
|
||||
result.Add (entity);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result.AsQueryable ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Search Filter on a Enumerable
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static async Task<IEnumerable<T>> ApplyFilterAsync<T, TKey> (
|
||||
this IEnumerable<T> source,
|
||||
string filter
|
||||
)
|
||||
where T : XBaseEntity<TKey> {
|
||||
//
|
||||
// Where Clause ...
|
||||
Expression<Func<T, bool>> whereClause = r => r
|
||||
.PropValuesContains (filter);
|
||||
|
||||
//
|
||||
// Generate Where Function ...
|
||||
var whereFunc = whereClause.Compile ();
|
||||
|
||||
//
|
||||
// Get Enumerable ...
|
||||
var enumerator = source
|
||||
.AsQueryable ()
|
||||
.AsAsyncEnumerable ();
|
||||
|
||||
//
|
||||
var result = new List<T> ();
|
||||
await
|
||||
foreach (var entity in enumerator) {
|
||||
//
|
||||
var isApproved = whereFunc (entity);
|
||||
if (isApproved) {
|
||||
result.Add (entity);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result
|
||||
.AsEnumerable ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Search Filter on a Enumerable
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> ApplyFilter<T> (
|
||||
this IEnumerable<T> source,
|
||||
string filter
|
||||
)
|
||||
where T : class {
|
||||
//
|
||||
// Apply Filter ...
|
||||
return source.Where (r => r.PropValuesContains (filter));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// provide a way to Order an IQueryable by string FieldName ...
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IOrderedQueryable<TSource> OrderBy<TSource> (
|
||||
this IQueryable<TSource> query,
|
||||
string propertyName
|
||||
) {
|
||||
//
|
||||
var entityType = typeof (TSource);
|
||||
|
||||
//
|
||||
// Create x => x.PropName
|
||||
var propertyInfo = entityType.GetProperty (propertyName);
|
||||
if (propertyInfo.IsNull ()) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
|
||||
//
|
||||
ParameterExpression arg = Expression.Parameter (entityType, "x");
|
||||
MemberExpression property = Expression.Property (arg, propertyName);
|
||||
var selector = Expression.Lambda (property, new ParameterExpression[] { arg });
|
||||
|
||||
//
|
||||
// Get System.Linq.Queryable.OrderBy() method.
|
||||
var enumarableType = typeof (System.Linq.Queryable);
|
||||
var method = enumarableType.GetMethods ()
|
||||
.Where (m => m.Name == "OrderBy" && m.IsGenericMethodDefinition)
|
||||
.Where (m => {
|
||||
//
|
||||
var parameters = m.GetParameters ().ToList ();
|
||||
|
||||
//
|
||||
// Put more restriction here to ensure selecting the right overload
|
||||
return parameters.Count == 2; // overload that has 2 parameters
|
||||
}).Single ();
|
||||
|
||||
//
|
||||
// The linq's OrderBy<TSource, TKey> has two generic types, which provided here ...
|
||||
MethodInfo genericMethod = method
|
||||
.MakeGenericMethod (entityType, propertyInfo.PropertyType);
|
||||
|
||||
/*
|
||||
Call query.OrderBy(selector), with query and selector: x=> x.PropName
|
||||
Note that we pass the selector as Expression to the method and we don't compile it.
|
||||
|
||||
By doing so EF can extract "order by" columns and generate SQL for it.
|
||||
*/
|
||||
var newQuery = (IOrderedQueryable<TSource>) genericMethod
|
||||
.Invoke (genericMethod, new object[] { query, selector });
|
||||
|
||||
//
|
||||
return newQuery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// provide a way to Order an IEnumerable by string FieldName ...
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IOrderedEnumerable<TSource> OrderBy<TSource> (
|
||||
this IEnumerable<TSource> query,
|
||||
string propertyName
|
||||
) {
|
||||
//
|
||||
var entityType = typeof (TSource);
|
||||
var propertyInfo = entityType.GetProperty (propertyName);
|
||||
if (propertyInfo.IsNull ()) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
|
||||
//
|
||||
return query.OrderBy (x => propertyInfo.GetValue (x, null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// provide a way to Order an IQueryable by string FieldName ...
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IOrderedQueryable<TSource> OrderByDescending<TSource> (
|
||||
this IQueryable<TSource> query,
|
||||
string propertyName
|
||||
) {
|
||||
//
|
||||
var entityType = typeof (TSource);
|
||||
|
||||
//
|
||||
// Create x => x.PropName
|
||||
var propertyInfo = entityType.GetProperty (propertyName);
|
||||
if (propertyInfo.IsNull ()) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
|
||||
//
|
||||
ParameterExpression arg = Expression.Parameter (entityType, "x");
|
||||
MemberExpression property = Expression.Property (arg, propertyName);
|
||||
var selector = Expression.Lambda (property, new ParameterExpression[] { arg });
|
||||
|
||||
//
|
||||
// Get System.Linq.Queryable.OrderBy() method.
|
||||
var enumarableType = typeof (System.Linq.Queryable);
|
||||
var method = enumarableType.GetMethods ()
|
||||
.Where (m => m.Name == "OrderByDescending" && m.IsGenericMethodDefinition)
|
||||
.Where (m => {
|
||||
//
|
||||
var parameters = m.GetParameters ().ToList ();
|
||||
|
||||
//
|
||||
// Put more restriction here to ensure selecting the right overload
|
||||
return parameters.Count == 2; // overload that has 2 parameters
|
||||
}).Single ();
|
||||
|
||||
//
|
||||
// The linq's OrderBy<TSource, TKey> has two generic types, which provided here ...
|
||||
MethodInfo genericMethod = method
|
||||
.MakeGenericMethod (entityType, propertyInfo.PropertyType);
|
||||
|
||||
/*
|
||||
Call query.OrderBy(selector), with query and selector: x=> x.PropName
|
||||
Note that we pass the selector as Expression to the method and we don't compile it.
|
||||
|
||||
By doing so EF can extract "order by" columns and generate SQL for it.
|
||||
*/
|
||||
var newQuery = (IOrderedQueryable<TSource>) genericMethod
|
||||
.Invoke (genericMethod, new object[] { query, selector });
|
||||
|
||||
//
|
||||
return newQuery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// provide a way to Order an IEnumerable by string FieldName ...
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IOrderedEnumerable<TSource> OrderByDescending<TSource> (
|
||||
this IEnumerable<TSource> query,
|
||||
string propertyName
|
||||
) {
|
||||
//
|
||||
var entityType = typeof (TSource);
|
||||
var propertyInfo = entityType.GetProperty (propertyName);
|
||||
if (propertyInfo.IsNull ()) {
|
||||
XException.InvalidArgs.Throw ();
|
||||
}
|
||||
|
||||
//
|
||||
return query.OrderByDescending (x => propertyInfo.GetValue (x, null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Sorting on a query
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="sortBy"></param>
|
||||
/// <param name="isAscending"></param>
|
||||
/// <param name="columnsMap"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IQueryable<T> ApplySorting<T, TKey> (
|
||||
this IQueryable<T> source,
|
||||
string sortBy,
|
||||
bool isAscending,
|
||||
IDictionary<string, Expression<Func<T, object>>> columnsMap = null
|
||||
)
|
||||
where T : XBaseEntity<TKey> {
|
||||
//
|
||||
if (columnsMap == null) {
|
||||
//
|
||||
columnsMap = source.FirstOrDefault ()
|
||||
.GetDefaultColumnsMap ();
|
||||
|
||||
//
|
||||
if (columnsMap == null) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Validate SortBy ...
|
||||
var sortByItem = columnsMap.Keys
|
||||
.FirstOrDefault (p =>
|
||||
p.ToNormalString () == sortBy.ToNormalString ());
|
||||
if (sortByItem.IsNullOrEmpty ()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
//
|
||||
// Apply Sorting ...
|
||||
if (isAscending) {
|
||||
return source.OrderBy (sortByItem);
|
||||
} else {
|
||||
return source.OrderByDescending (sortByItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Sorting on a query
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="sortBy"></param>
|
||||
/// <param name="isAscending"></param>
|
||||
/// <param name="columnsMap"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> ApplySorting<T, TKey> (
|
||||
this IEnumerable<T> source,
|
||||
string sortBy,
|
||||
bool isAscending,
|
||||
IDictionary<string, Expression<Func<T, object>>> columnsMap = null
|
||||
)
|
||||
where T : XBaseEntity<TKey> {
|
||||
//
|
||||
if (columnsMap == null) {
|
||||
//
|
||||
columnsMap = source.FirstOrDefault ()
|
||||
.GetDefaultColumnsMap ();
|
||||
|
||||
//
|
||||
if (columnsMap == null) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Validate SortBy ...
|
||||
var sortByItem = columnsMap.Keys
|
||||
.FirstOrDefault (p =>
|
||||
p.ToNormalString () == sortBy.ToNormalString ());
|
||||
if (sortByItem.IsNullOrEmpty ()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
//
|
||||
// Apply Sorting ...
|
||||
if (isAscending) {
|
||||
return source.OrderBy (sortByItem);
|
||||
} else {
|
||||
return source.OrderByDescending (sortByItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Sorting on a Enumerable
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="sortBy"></param>
|
||||
/// <param name="isAscending"></param>
|
||||
/// <param name="columnsMap"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> ApplySorting<T> (
|
||||
this IEnumerable<T> source,
|
||||
string sortBy,
|
||||
bool isAscending,
|
||||
IDictionary<string, Expression<Func<T, object>>> columnsMap = null
|
||||
)
|
||||
where T : class {
|
||||
//
|
||||
if (columnsMap == null) {
|
||||
//
|
||||
columnsMap = source.FirstOrDefault ()
|
||||
.GetDefaultColumnsMap ();
|
||||
|
||||
//
|
||||
if (columnsMap == null) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Validate SortBy ...
|
||||
var sortByItem = columnsMap.Keys
|
||||
.FirstOrDefault (p =>
|
||||
p.ToNormalString () == sortBy.ToNormalString ());
|
||||
if (sortByItem.IsNullOrEmpty ()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
//
|
||||
// Apply Sorting ...
|
||||
if (isAscending) {
|
||||
return source.OrderBy (sortByItem);
|
||||
} else {
|
||||
return source.OrderByDescending (sortByItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Paging on a qury
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IQueryable<T> ApplyPaging<T> (
|
||||
this IQueryable<T> source,
|
||||
int? page,
|
||||
int? pageSize
|
||||
) {
|
||||
//
|
||||
if (!page.HasValue ||
|
||||
page <= 0) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
//
|
||||
if (!pageSize.HasValue ||
|
||||
pageSize <= 0 ||
|
||||
pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) {
|
||||
pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
|
||||
//
|
||||
var pageVal = page.GetValueOrDefault ();
|
||||
var pageSizeVal = pageSize.GetValueOrDefault ();
|
||||
|
||||
//
|
||||
return source
|
||||
.Skip ((pageVal - 1) * pageSizeVal)
|
||||
.Take (pageSizeVal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Paging on a Enumerable
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> ApplyPaging<T> (
|
||||
this IEnumerable<T> source,
|
||||
int? page,
|
||||
int? pageSize
|
||||
) {
|
||||
//
|
||||
if (!page.HasValue ||
|
||||
page <= 0) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
//
|
||||
if (!pageSize.HasValue ||
|
||||
pageSize <= 0 ||
|
||||
pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) {
|
||||
pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
|
||||
//
|
||||
var pageVal = page.GetValueOrDefault ();
|
||||
var pageSizeVal = pageSize.GetValueOrDefault ();
|
||||
|
||||
//
|
||||
return source
|
||||
.Skip ((pageVal - 1) * pageSizeVal)
|
||||
.Take (pageSizeVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System.Linq;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Configuration;
|
||||
using xDataService.Constants;
|
||||
|
||||
namespace xDataService.Extensions {
|
||||
public static class XDataServiceConfigurationExtensions {
|
||||
/// <summary>
|
||||
/// Extract Provider enum from Provider Type ...
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static XDbProviders ToDbProvider (this string source) {
|
||||
//
|
||||
if (source.IsNullOrEmpty ()) {
|
||||
return XDbProviders.None;
|
||||
}
|
||||
|
||||
//
|
||||
source = source.ToNormalString ();
|
||||
|
||||
//
|
||||
var result = source == ProviderType.MySQL.ToNormalString () ?
|
||||
XDbProviders.MySQL :
|
||||
source == ProviderType.SQLite.ToNormalString () ?
|
||||
XDbProviders.SQLite :
|
||||
source == ProviderType.SQLServer.ToNormalString () ?
|
||||
XDbProviders.SQLServer :
|
||||
source == ProviderType.MongoDB.ToNormalString () ?
|
||||
XDbProviders.MongoDB :
|
||||
XDbProviders.None;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Mongo URI from ConnectionString
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetMongoDbURI (this XDataServiceConfiguration source) {
|
||||
//
|
||||
var parts = source.GetConnectionParts ();
|
||||
var result = parts
|
||||
.Where (p => p.Trim ().StartsWith ("Uri="))
|
||||
.FirstOrDefault ();
|
||||
result = result.Replace ("Uri=", "").Trim ();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Mongo Database Name from Connaction string
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetMongoDbDatabase (this XDataServiceConfiguration source) {
|
||||
//
|
||||
var parts = source.GetConnectionParts ();
|
||||
var result = parts
|
||||
.Where (p => p.Trim ().StartsWith ("Database="))
|
||||
.FirstOrDefault ();
|
||||
result = result.Replace ("Database=", "").Trim ();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
#region Private ...
|
||||
private static string[] GetConnectionParts (this XDataServiceConfiguration source) {
|
||||
//
|
||||
var result = source.ConnectionString.Split (';');
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using GraphQL;
|
||||
using GraphQL.Types;
|
||||
using xDataService.Constants;
|
||||
using xModels.Dtos;
|
||||
|
||||
namespace xDataService.Extensions {
|
||||
public static class XGraphQLExtensions {
|
||||
public static TKey GetIdArgument<TKey> (this IResolveFieldContext<object> source) {
|
||||
return source.GetArgument<TKey> (XGraphQLHelper.GetIdArgument<IntGraphType>().Name);
|
||||
}
|
||||
|
||||
public static bool GetIgnoreSoftDeletedArgument<T> (this IResolveFieldContext<T> source) {
|
||||
return source.GetArgument<bool> (XGraphQLHelper.IgnoreSoftDeletedArgument.Name);
|
||||
}
|
||||
|
||||
public static bool GetContainsDetailArgument<T> (this IResolveFieldContext<T> source) {
|
||||
return source.GetArgument<bool> (XGraphQLHelper.ContainsDetailArgument.Name);
|
||||
}
|
||||
|
||||
public static string GetSearchQueryArgument<T> (this IResolveFieldContext<T> source) {
|
||||
return source.GetArgument<string> (XGraphQLHelper.SearchQueryArgument.Name);
|
||||
}
|
||||
|
||||
public static XQuery GetQueryArgument<T> (this IResolveFieldContext<T> source) {
|
||||
return source.GetArgument<XQuery> (XGraphQLHelper.QueryArgument.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Core.Bindings;
|
||||
using MongoDB.Driver.Core.Clusters;
|
||||
using MongoDB.Driver.Core.Servers;
|
||||
using MongoDB.Driver.Core.WireProtocol.Messages.Encoders;
|
||||
using MongoDB.Driver.Linq;
|
||||
using xDataService.Mongo;
|
||||
|
||||
namespace xDataService.Extensions {
|
||||
public static class XMongoDbExtensions {
|
||||
public static Guid ToGuid (this ObjectId source) {
|
||||
//
|
||||
var bytes = source
|
||||
.ToByteArray ()
|
||||
.Concat (new byte[] { 5, 5, 5, 5 })
|
||||
.ToArray ();
|
||||
|
||||
//
|
||||
var result = new Guid (bytes);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ObjectId ToObjectId (this Guid source) {
|
||||
//
|
||||
var bytes = source
|
||||
.ToByteArray ()
|
||||
.Take (12)
|
||||
.ToArray ();
|
||||
|
||||
//
|
||||
var result = new ObjectId (bytes);
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IAsyncEnumerable<T> ToAsyncEnumerable<T> (this IAsyncCursorSource<T> asyncCursorSource) {
|
||||
return new XAsyncEnumerableAdapter<T> (asyncCursorSource);
|
||||
}
|
||||
|
||||
public static async IAsyncEnumerable<T> ToAsyncEnumerable<T> (this IAsyncCursor<T> source) {
|
||||
while (await source.MoveNextAsync ()) {
|
||||
foreach (var current in source.Current) {
|
||||
yield return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static XMongoAsyncCursor<T> ToAsyncCursor<T> (this IQueryable<T> source) {
|
||||
return new XMongoAsyncCursor<T> (source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using GraphQL.Types;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.GraphQL
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Entity GraphType Object ...
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public abstract class XBaseGraphObjectType<T, TKey> : ObjectGraphType<T>
|
||||
where T : XBaseEntity<TKey> {
|
||||
public XBaseGraphObjectType () {
|
||||
//
|
||||
Name = $"{typeof(T).Name}GraphType";
|
||||
|
||||
//
|
||||
Field (x => x.Id);
|
||||
Field (x => x.Deleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using GraphQL.Types;
|
||||
using xDataService.Models;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.GraphQL {
|
||||
public class XBaseGraphQLEventType<TEntity, TKey, TGraphType> : ObjectGraphType<XBaseEventModel<TEntity>>
|
||||
where TGraphType : IGraphType
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
public XBaseGraphQLEventType () {
|
||||
//
|
||||
Name = nameof (XBaseGraphQLEventType<TEntity, TKey, TGraphType>);
|
||||
|
||||
//
|
||||
Field (x => x.Model, type : typeof (TGraphType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using GraphQL.Types;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Configuration;
|
||||
using xDataService.Constants;
|
||||
using xDataService.Extensions;
|
||||
using xDataService.Interfaces;
|
||||
using xDataService.Models;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.GraphQL {
|
||||
public abstract class XBaseGraphQLQuery<TEntity, TKey, TGraph, TGraphKey> : ObjectGraphType, IXBaseGraphQLQuery<TEntity, TKey, TGraph, TGraphKey>
|
||||
where TEntity : XBaseEntity<TKey>
|
||||
where TGraph : IGraphType
|
||||
where TGraphKey : IGraphType {
|
||||
//
|
||||
private readonly XDataServiceConfiguration configuration;
|
||||
private readonly IXBaseRepository<TEntity, TKey> repository;
|
||||
private readonly IXBaseGraphQLTypeHelper<TEntity, TKey> helper;
|
||||
|
||||
//
|
||||
public XBaseGraphQLQuery (
|
||||
XDataServiceConfiguration configuration,
|
||||
IXBaseRepository<TEntity, TKey> repository,
|
||||
IXBaseGraphQLTypeHelper<TEntity, TKey> helper
|
||||
) {
|
||||
//
|
||||
this.helper = helper;
|
||||
this.repository = repository;
|
||||
this.configuration = configuration;
|
||||
|
||||
//
|
||||
Name = GetType ().Name;
|
||||
|
||||
//
|
||||
#region Retrieve ...
|
||||
//
|
||||
// Get ...
|
||||
FieldAsync<TGraph> (
|
||||
name: helper.GetInQuerySingleName (),
|
||||
arguments: new QueryArguments (
|
||||
XGraphQLHelper.GetIdArgument<TGraphKey> (),
|
||||
XGraphQLHelper.IgnoreSoftDeletedArgument,
|
||||
XGraphQLHelper.ContainsDetailArgument
|
||||
),
|
||||
resolve : async context =>
|
||||
await repository
|
||||
.GetAsync (
|
||||
id: context.GetIdArgument<TKey> (),
|
||||
ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (),
|
||||
containsDetail: context.GetContainsDetailArgument ()
|
||||
)
|
||||
.ToDynamicObject ()
|
||||
);
|
||||
|
||||
//
|
||||
// GetAll ...
|
||||
FieldAsync<ListGraphType<TGraph>> (
|
||||
name: helper.GetInQueryCollectionName (),
|
||||
arguments: new QueryArguments (
|
||||
XGraphQLHelper.IgnoreSoftDeletedArgument,
|
||||
XGraphQLHelper.ContainsDetailArgument
|
||||
),
|
||||
resolve : async context =>
|
||||
await repository
|
||||
.GetAllAsync (
|
||||
ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (),
|
||||
containsDetail: context.GetContainsDetailArgument ()
|
||||
)
|
||||
.ToDynamicObject ()
|
||||
);
|
||||
|
||||
//
|
||||
// FindOne ...
|
||||
FieldAsync<TGraph> (
|
||||
name: helper.GetFindOneName (),
|
||||
arguments: new QueryArguments (
|
||||
XGraphQLHelper.SearchQueryArgument,
|
||||
XGraphQLHelper.IgnoreSoftDeletedArgument,
|
||||
XGraphQLHelper.ContainsDetailArgument
|
||||
),
|
||||
resolve : async (context) => {
|
||||
//
|
||||
var searchQuery = context.GetSearchQueryArgument ();
|
||||
Expression<Func<TEntity, bool>> whereClase = pe => pe
|
||||
.PropValuesContains (searchQuery);
|
||||
|
||||
//
|
||||
return await repository
|
||||
.FindOneAsync (
|
||||
whereClause: whereClase,
|
||||
ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (),
|
||||
containsDetail: context.GetContainsDetailArgument ()
|
||||
)
|
||||
.ToDynamicObject ();
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
// FindMany ...
|
||||
FieldAsync<ListGraphType<TGraph>> (
|
||||
name: helper.GetFindManyName (),
|
||||
arguments: new QueryArguments (
|
||||
XGraphQLHelper.SearchQueryArgument,
|
||||
XGraphQLHelper.IgnoreSoftDeletedArgument,
|
||||
XGraphQLHelper.ContainsDetailArgument
|
||||
),
|
||||
resolve : async (context) => {
|
||||
//
|
||||
var searchQuery = context.GetSearchQueryArgument ();
|
||||
Expression<Func<TEntity, bool>> whereClase = pe => pe
|
||||
.PropValuesContains (searchQuery);
|
||||
|
||||
//
|
||||
return await repository
|
||||
.FindManyAsync (
|
||||
whereClause: whereClase,
|
||||
ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (),
|
||||
containsDetail: context.GetContainsDetailArgument ()
|
||||
)
|
||||
.ToDynamicObject ();
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
// Query ...
|
||||
FieldAsync<XGraphQueryResult<TEntity, TGraph>> (
|
||||
name: helper.GetQueryName (),
|
||||
arguments: new QueryArguments (
|
||||
XGraphQLHelper.QueryArgument,
|
||||
XGraphQLHelper.IgnoreSoftDeletedArgument
|
||||
),
|
||||
resolve : async context =>
|
||||
await repository
|
||||
.QueryAsync (
|
||||
query: context.GetQueryArgument (),
|
||||
ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument ()
|
||||
)
|
||||
.ToDynamicObject ()
|
||||
);
|
||||
|
||||
//
|
||||
// Count ...
|
||||
FieldAsync<IntGraphType> (
|
||||
name: helper.GetCountName (),
|
||||
resolve: async context =>
|
||||
await repository
|
||||
.CountAsync ()
|
||||
);
|
||||
|
||||
//
|
||||
// Exists ...
|
||||
FieldAsync<BooleanGraphType> (
|
||||
name: helper.GetExistsName (),
|
||||
arguments: new QueryArguments (
|
||||
XGraphQLHelper.GetIdArgument<TGraphKey> ()
|
||||
),
|
||||
resolve : async context =>
|
||||
await repository.IsExistsAsync (
|
||||
context.GetIdArgument<TKey> ()
|
||||
)
|
||||
);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Collections.Generic;
|
||||
using GraphQL.Resolvers;
|
||||
using GraphQL.Types;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Events;
|
||||
using xDataService.Interfaces;
|
||||
using xDataService.Models;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.GraphQL {
|
||||
public abstract class XBaseGraphQLSubscription<TEntity, TKey, TGraph> : ObjectGraphType
|
||||
where TGraph : IGraphType
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
public XBaseGraphQLSubscription (IXBaseRepositoryEvents<TEntity> eventProvider = null) {
|
||||
//
|
||||
Name = GetType ().Name;
|
||||
|
||||
//
|
||||
// Check if base repository patter registered ...
|
||||
if (!eventProvider.IsNull ()) {
|
||||
//
|
||||
// Add ...
|
||||
AddField (new EventStreamFieldType {
|
||||
//
|
||||
Name = nameof (XBaseRepositoryEvents<TEntity>.AddEvent),
|
||||
Type = typeof (XBaseGraphQLEventType<TEntity, TKey, TGraph>),
|
||||
Resolver = new FuncFieldResolver<XBaseEventModel<TEntity>> (
|
||||
context => context.Source as XBaseEventModel<TEntity>
|
||||
),
|
||||
Subscriber = new EventStreamResolver<XBaseEventModel<TEntity>> (context => {
|
||||
return eventProvider.OnAddObservable;
|
||||
})
|
||||
});
|
||||
|
||||
//
|
||||
// Add Many ...
|
||||
AddField (new EventStreamFieldType {
|
||||
//
|
||||
Name = nameof (XBaseRepositoryEvents<TEntity>.AddManyEvent),
|
||||
Type = typeof (XBaseGraphQLEventType<TEntity, TKey, TGraph>),
|
||||
Resolver = new FuncFieldResolver<XBaseEventModel<IEnumerable<TEntity>>> (
|
||||
context => context.Source as XBaseEventModel<IEnumerable<TEntity>>
|
||||
),
|
||||
Subscriber = new EventStreamResolver<XBaseEventModel<IEnumerable<TEntity>>> (context => {
|
||||
return eventProvider.OnAddManyObservable;
|
||||
})
|
||||
});
|
||||
|
||||
//
|
||||
// Update ...
|
||||
AddField (new EventStreamFieldType {
|
||||
//
|
||||
Name = nameof (XBaseRepositoryEvents<TEntity>.UpdateEvent),
|
||||
Type = typeof (XBaseGraphQLEventType<TEntity, TKey, TGraph>),
|
||||
Resolver = new FuncFieldResolver<XBaseEventModel<TEntity>> (
|
||||
context => context.Source as XBaseEventModel<TEntity>
|
||||
),
|
||||
Subscriber = new EventStreamResolver<XBaseEventModel<TEntity>> (context => {
|
||||
return eventProvider.OnUpdateObservable;
|
||||
})
|
||||
});
|
||||
|
||||
//
|
||||
// Update Mnay ...
|
||||
AddField (new EventStreamFieldType {
|
||||
//
|
||||
Name = nameof (XBaseRepositoryEvents<TEntity>.UpdateManyEvent),
|
||||
Type = typeof (XBaseGraphQLEventType<TEntity, TKey, TGraph>),
|
||||
Resolver = new FuncFieldResolver<XBaseEventModel<IEnumerable<TEntity>>> (
|
||||
context => context.Source as XBaseEventModel<IEnumerable<TEntity>>
|
||||
),
|
||||
Subscriber = new EventStreamResolver<XBaseEventModel<IEnumerable<TEntity>>> (context => {
|
||||
return eventProvider.OnUpdateManyObservable;
|
||||
})
|
||||
});
|
||||
|
||||
//
|
||||
// Remove ...
|
||||
AddField (new EventStreamFieldType {
|
||||
//
|
||||
Name = nameof (XBaseRepositoryEvents<TEntity>.RemoveEvent),
|
||||
Type = typeof (XBaseGraphQLEventType<TEntity, TKey, TGraph>),
|
||||
Resolver = new FuncFieldResolver<XBaseEventModel<TEntity>> (
|
||||
context => context.Source as XBaseEventModel<TEntity>
|
||||
),
|
||||
Subscriber = new EventStreamResolver<XBaseEventModel<TEntity>> (context => {
|
||||
return eventProvider.OnRemoveObservable;
|
||||
})
|
||||
});
|
||||
|
||||
//
|
||||
// Remove Many ...
|
||||
AddField (new EventStreamFieldType {
|
||||
//
|
||||
Name = nameof (XBaseRepositoryEvents<TEntity>.RemoveManyEvent),
|
||||
Type = typeof (XBaseGraphQLEventType<TEntity, TKey, TGraph>),
|
||||
Resolver = new FuncFieldResolver<XBaseEventModel<IEnumerable<TEntity>>> (
|
||||
context => context.Source as XBaseEventModel<IEnumerable<TEntity>>
|
||||
),
|
||||
Subscriber = new EventStreamResolver<XBaseEventModel<IEnumerable<TEntity>>> (context => {
|
||||
return eventProvider.OnRemoveManyObservable;
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Configuration;
|
||||
using xDataService.Interfaces;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.GraphQL {
|
||||
public abstract class XBaseGraphQLTypeHelper<TEntity, TKey> : IXBaseGraphQLTypeHelper<TEntity, TKey>
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
//
|
||||
private readonly XDataServiceConfiguration configuration;
|
||||
|
||||
//
|
||||
public XBaseGraphQLTypeHelper (
|
||||
XDataServiceConfiguration configuration = null
|
||||
) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
//
|
||||
public abstract string GetInQueryCollectionName ();
|
||||
|
||||
public abstract string GetInQuerySingleName ();
|
||||
|
||||
//
|
||||
public string GetFindOneName () {
|
||||
return $"find{GetInQuerySingleName ().ToNormalString().Capitalize()}";
|
||||
}
|
||||
|
||||
public string GetFindManyName () {
|
||||
return $"find{GetInQueryCollectionName ().ToNormalString().Capitalize()}";
|
||||
}
|
||||
|
||||
public string GetQueryName () {
|
||||
return $"query{GetInQueryCollectionName().ToNormalString().Capitalize()}";
|
||||
}
|
||||
|
||||
public string GetCountName () {
|
||||
return $"count{GetInQueryCollectionName().ToNormalString().Capitalize()}";
|
||||
}
|
||||
|
||||
public string GetExistsName () {
|
||||
return $"exists{GetInQuerySingleName().ToNormalString().Capitalize()}";
|
||||
}
|
||||
|
||||
public string GetGraphQLPath (string baseGraphPath = null) {
|
||||
//
|
||||
baseGraphPath = baseGraphPath.IsNullOrEmpty () ?
|
||||
configuration.IsNull () || configuration.GraphQLBasePath.IsNullOrEmpty () ?
|
||||
"" :
|
||||
configuration.GraphQLBasePath :
|
||||
baseGraphPath;
|
||||
|
||||
//
|
||||
var basePath = baseGraphPath
|
||||
.EndsWith ("/") ?
|
||||
baseGraphPath
|
||||
.Substring (0, baseGraphPath.Length - 1) :
|
||||
baseGraphPath;
|
||||
|
||||
//
|
||||
return $"{basePath}/{GetInQueryCollectionName().ToNormalString()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using xCommons.Extensions;
|
||||
|
||||
namespace xDataService.Helpers {
|
||||
public partial class XCursorHelper {
|
||||
public static string ToCursor<T> (T id) {
|
||||
//
|
||||
// Check Numbers ...
|
||||
if (id.ToString ().IsDigits ()) {
|
||||
return Convert
|
||||
.ToBase64String (BitConverter
|
||||
.GetBytes (Convert.ToInt32 (id))
|
||||
);
|
||||
} else
|
||||
//
|
||||
// Check Guid ...
|
||||
if (id.ToString ().IsGuid ()) {
|
||||
return Convert
|
||||
.ToBase64String (new Guid (id.ToString ())
|
||||
.ToByteArray ());
|
||||
} else
|
||||
//
|
||||
// Assume as String ...
|
||||
{
|
||||
return Convert
|
||||
.ToBase64String (id
|
||||
.ToString ().ToBytes ());
|
||||
}
|
||||
}
|
||||
|
||||
public static T FromCursor<T> (string cursor) {
|
||||
//
|
||||
// Check Numbers ...
|
||||
var returnType = typeof (T);
|
||||
if (returnType == typeof (int) ||
|
||||
returnType == typeof (long) ||
|
||||
returnType == typeof (decimal)) {
|
||||
return Convert.ChangeType ((BitConverter
|
||||
.ToInt32 (Convert
|
||||
.FromBase64String (cursor),
|
||||
0
|
||||
))
|
||||
.ToDynamicObject (), returnType);
|
||||
} else
|
||||
//
|
||||
// Check Guid ...
|
||||
if (returnType == typeof (Guid)) {
|
||||
return Convert.ChangeType ((new Guid (
|
||||
Convert.FromBase64String (cursor)
|
||||
))
|
||||
.ToDynamicObject (), returnType);
|
||||
} else
|
||||
//
|
||||
// Assume as String ...
|
||||
{
|
||||
return Convert.ChangeType ((
|
||||
Convert.FromBase64String (cursor)
|
||||
)
|
||||
.ToDynamicObject (), returnType);
|
||||
}
|
||||
}
|
||||
|
||||
public static (string firstCursor, string lastCursor) GetFirstAndLastCursor<T> (IEnumerable<T> ids) {
|
||||
//
|
||||
if (ids?.Any () != true) {
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
//
|
||||
var firstCursor = ToCursor (ids.First ());
|
||||
var lastCursor = ToCursor (ids.Last ());
|
||||
|
||||
//
|
||||
return (firstCursor, lastCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using GraphQL.Types;
|
||||
using xDataService.Models;
|
||||
using xModels.Dtos;
|
||||
|
||||
namespace xDataService.Constants {
|
||||
public static class XGraphQLHelper {
|
||||
public static QueryArgument<TKey> GetIdArgument<TKey> ()
|
||||
where TKey : IGraphType {
|
||||
return new QueryArgument<TKey> {
|
||||
Name = "id",
|
||||
Description = "the id of entity, which you want to retrieve ..."
|
||||
};
|
||||
}
|
||||
|
||||
public static QueryArgument<BooleanGraphType> IgnoreSoftDeletedArgument = new QueryArgument<BooleanGraphType> {
|
||||
Name = "ignoreSoftDeleteds",
|
||||
DefaultValue = true,
|
||||
Description = "determines result should ignore soft deleted items or not ..."
|
||||
};
|
||||
|
||||
public static QueryArgument<BooleanGraphType> ContainsDetailArgument = new QueryArgument<BooleanGraphType> {
|
||||
Name = "containsDetail",
|
||||
DefaultValue = false,
|
||||
Description = "determines result should contains navigation properties or not ..."
|
||||
};
|
||||
|
||||
public static QueryArgument<StringGraphType> SearchQueryArgument = new QueryArgument<StringGraphType> {
|
||||
Name = "searchQuery",
|
||||
Description = "the value which had to looking for ..."
|
||||
};
|
||||
|
||||
public static QueryArgument<XGraphQueryType> QueryArgument = new QueryArgument<XGraphQueryType> {
|
||||
Name = "query",
|
||||
DefaultValue = new XQuery (),
|
||||
Description = "determines query structure info for retrieving data ..."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using GraphQL.Types;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
public interface IXBaseGraphQLQuery<TEntity, TKey, TGraph, TGraphKey>
|
||||
where TEntity : XBaseEntity<TKey>
|
||||
where TGraph : IGraphType
|
||||
where TGraphKey : IGraphType { }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
public interface IXBaseGraphQLTypeHelper<TEntity, TKey>
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
string GetInQuerySingleName ();
|
||||
string GetInQueryCollectionName ();
|
||||
|
||||
string GetFindOneName ();
|
||||
|
||||
string GetFindManyName ();
|
||||
|
||||
string GetQueryName ();
|
||||
|
||||
string GetCountName ();
|
||||
|
||||
string GetExistsName ();
|
||||
|
||||
string GetGraphQLPath (string baseGraphPath = null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using xModels.Base;
|
||||
using xModels.Dtos;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
/// <summary>
|
||||
/// a Base Repository Interface for Manipulate
|
||||
/// an Entity in DataBase
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IXBaseRepository<T, TKey> : IDisposable
|
||||
where T : XBaseEntity<TKey> {
|
||||
//
|
||||
#region Add ...
|
||||
/// <summary>
|
||||
/// add a new Entity ...
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> AddAsync (
|
||||
T item,
|
||||
bool saveChanges = true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// add or update an Entity (add if not exists/update if exists) ...
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> AddOrUpdateAsync (
|
||||
T item,
|
||||
bool saveChanges = true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// add a range of new Entities ...
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task AddRangeAsync (
|
||||
IEnumerable<T> items,
|
||||
bool saveChanges = true
|
||||
);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Remove ...
|
||||
/// <summary>
|
||||
/// remove an Entity by it's Id ...
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="softDelete"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> RemoveAsync (
|
||||
TKey id,
|
||||
bool softDelete = true,
|
||||
bool saveChanges = true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// remove an Entity ...
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="softDelete"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> RemoveAsync (
|
||||
T item,
|
||||
bool softDelete = true,
|
||||
bool saveChanges = true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// remove a range of exists Entities ...
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="softDelete"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task RemoveRangeAsync (
|
||||
IEnumerable<T> items,
|
||||
bool softDelete = true,
|
||||
bool saveChanges = true
|
||||
);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Retrieve ...
|
||||
/// <summary>
|
||||
/// retrieve whole items as queryable ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IQueryable<T> AsQueryable ();
|
||||
|
||||
/// <summary>
|
||||
/// retrieve an Entity by it's Id ...
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <param name="containsDetail"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> GetAsync (
|
||||
TKey id,
|
||||
bool ignoreSoftDeleteds = true,
|
||||
bool containsDetail = false
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// retrieve all exists Entities ...
|
||||
/// </summary>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <param name="containsDetail"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<T>> GetAllAsync (
|
||||
bool ignoreSoftDeleteds = true,
|
||||
bool containsDetail = false
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// find an Entity by providing a Conditional Expression ...
|
||||
/// </summary>
|
||||
/// <param name="whereClause"></param>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <param name="containsDetail"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> FindOneAsync (
|
||||
Expression<Func<T, bool>> whereClause,
|
||||
bool ignoreSoftDeleteds = true,
|
||||
bool containsDetail = false
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// find a collection of Entities by proving a Conditional Expression ...
|
||||
/// </summary>
|
||||
/// <param name="whereClause"></param>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <param name="containsDetail"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<T>> FindManyAsync (
|
||||
Expression<Func<T, bool>> whereClause,
|
||||
bool ignoreSoftDeleteds = true,
|
||||
bool containsDetail = false
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// retrieve Entities based on XQuery Pagination structure ...
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <returns></returns>
|
||||
Task<XQueryResult<T>> QueryAsync (
|
||||
XQuery query,
|
||||
bool ignoreSoftDeleteds = true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// retrieve Entities based on XQuery Pagination structure by providing a Conditional Expression ...
|
||||
/// </summary>
|
||||
/// <param name="whereClause"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <returns></returns>
|
||||
Task<XQueryResult<T>> ConditionalQueryAsync (
|
||||
Expression<Func<T, bool>> whereClause,
|
||||
XQuery query,
|
||||
bool ignoreSoftDeleteds = true
|
||||
);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Update ...
|
||||
/// <summary>
|
||||
/// Update an Entity values ...
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> UpdateAsync (
|
||||
TKey id,
|
||||
T item,
|
||||
bool saveChanges = true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Update a range of Entities ...
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="saveChanges"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> UpdateRangeAsync (
|
||||
IEnumerable<T> items,
|
||||
bool saveChanges = true
|
||||
);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Count ...
|
||||
/// <summary>
|
||||
/// count all exists Entities ...
|
||||
/// </summary>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> CountAsync (bool ignoreSoftDeleteds = true);
|
||||
|
||||
/// <summary>
|
||||
/// count all exists Entities Pages by providing page size ...
|
||||
/// </summary>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalItems"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> PagesCountAsync (
|
||||
int pageSize,
|
||||
int? totalItems = null
|
||||
);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Exists ...
|
||||
/// <summary>
|
||||
/// Check an Entity exists or not ...
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="ignoreSoftDeleteds"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsExistsAsync (
|
||||
TKey id,
|
||||
bool ignoreSoftDeleteds = true
|
||||
);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Unit Of Work ...
|
||||
/// <summary>
|
||||
/// Save all unsaved Transactions on DbContext ...
|
||||
/// used fo Unit Of Works Design Pattern ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<int> SaveChangesAsync ();
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Key ...
|
||||
TKey GetKey (T item);
|
||||
|
||||
void SetKey (
|
||||
ref T item,
|
||||
TKey id
|
||||
);
|
||||
|
||||
Task<T> HandleKeyAsync (T item);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Detach ...
|
||||
void Detach (T item);
|
||||
|
||||
void Detach (IEnumerable<T> items);
|
||||
|
||||
void Detach (XQueryResult<T> query);
|
||||
|
||||
void Detach (XPageResponse<T> page);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using xDataService.Models;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
public interface IXBaseRepositoryEvents<T> {
|
||||
//
|
||||
void AddEvent (XBaseEventModel<T> model);
|
||||
void UpdateEvent (XBaseEventModel<T> model);
|
||||
void RemoveEvent (XBaseEventModel<T> model);
|
||||
|
||||
//
|
||||
void AddManyEvent (XBaseEventModel<IEnumerable<T>> model);
|
||||
void UpdateManyEvent (XBaseEventModel<IEnumerable<T>> model);
|
||||
void RemoveManyEvent (XBaseEventModel<IEnumerable<T>> model);
|
||||
|
||||
//
|
||||
IObservable<XBaseEventModel<T>> OnAddObservable { get; }
|
||||
IObservable<XBaseEventModel<T>> OnUpdateObservable { get; }
|
||||
IObservable<XBaseEventModel<T>> OnRemoveObservable { get; }
|
||||
|
||||
//
|
||||
IObservable<XBaseEventModel<IEnumerable<T>>> OnAddManyObservable { get; }
|
||||
IObservable<XBaseEventModel<IEnumerable<T>>> OnUpdateManyObservable { get; }
|
||||
IObservable<XBaseEventModel<IEnumerable<T>>> OnRemoveManyObservable { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDB.Bson.Serialization.Conventions;
|
||||
using xDataService.Constants;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
public interface IXDataServiceHelper {
|
||||
/// <summary>
|
||||
/// register DbContext implementation on DI Container ...
|
||||
/// Only Used in EFCore ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="connectionString"></param>
|
||||
/// <param name="dbProvider"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <param name="optionsBuilder"></param>
|
||||
void AddDbContext (
|
||||
IServiceCollection services,
|
||||
string connectionString,
|
||||
XDbProviders dbProvider,
|
||||
ServiceLifetime lifetime,
|
||||
Action<dynamic> optionsBuilder = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// register all Entities Repositories and Events on DI Container ...
|
||||
/// implementations of IXBaseRepository and IXBaseRepositoryEvent ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
void AddRepositories (
|
||||
IServiceCollection services,
|
||||
ServiceLifetime lifetime
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Regitster KeyGenerators for Repositories ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
void AddKeyGenerators (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// register DBSeeder Configuration on DI Container ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="configuration"></param>
|
||||
void AddDbSeederConfiguration (
|
||||
IServiceCollection services,
|
||||
IConfiguration configuration
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// register Data Seeder on DI Container ...
|
||||
/// implementation of IXDbSeeder ...
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="lifetime"></param>
|
||||
/// <typeparam name="TDbSeeder"></typeparam>
|
||||
void AddDbSeeder<TDbSeeder> (
|
||||
IServiceCollection services,
|
||||
IConfiguration config,
|
||||
ServiceLifetime lifetime
|
||||
) where TDbSeeder : IXDbSeeder;
|
||||
|
||||
/// <summary>
|
||||
/// Set Mongo Convention Pack ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
ConventionPack MongoConventionPacks ();
|
||||
|
||||
/// <summary>
|
||||
/// Configure all required MongoDb Class Mappers and etc here ...
|
||||
/// </summary>
|
||||
void RegisterMongoExtras ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Threading.Tasks;
|
||||
using xDataService.Configuration;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
/// <summary>
|
||||
/// Db Seeder used to seed data on Database on startup time ...
|
||||
/// </summary>
|
||||
public interface IXDbSeeder {
|
||||
IXDbSeederConfig Config { get; }
|
||||
XDataServiceConfiguration DataServiceConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// do all seeding action by helping of Repositories here ...
|
||||
/// don't forget to check UpdateExists value on IXDbSeederConfig for prevent system of duplicate Entity adding on startups ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task Seed ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace xDataService.Interfaces {
|
||||
/// <summary>
|
||||
/// this Configuration used to Carry all Entity Descriptors collections
|
||||
/// can readable by appSettings.json file for providing dynamic data seeding ...
|
||||
/// </summary>
|
||||
public interface IXDbSeederConfig {
|
||||
/// <summary>
|
||||
/// determines an Entity can Update when Exists or not ...
|
||||
/// this must handled by consumer on IXDbSeeder Seed method implementation ...
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
bool UpdateExists { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using GraphQL.Server;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
/// <summary>
|
||||
/// this is a helper class which provides all requirements to providing GraphQL
|
||||
/// based data manipulating mechanism ...
|
||||
/// </summary>
|
||||
public interface IXGraphQLHelper {
|
||||
/// <summary>
|
||||
/// Register all GraphQL Enum Types ...
|
||||
/// Enum Types use to Map string values to Enum values in propper way and vise versa in GraphQL ...
|
||||
/// must extended from EnumerationGraphType ...
|
||||
/// </summary>
|
||||
void AddXGraphEnumTypes (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Register all GraphQL Object Types ...
|
||||
/// GraphQL works based on Types, so for representing each data you have to define a Type ...
|
||||
/// must extended from ObjectGraphType<T> or XBaseGraphObjectType<TKey>
|
||||
/// </summary>
|
||||
void AddXGraphObjectTypes (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Register all GraphQL Input Types ...
|
||||
/// InputTypes represent recieving data structure in GraphQL for doing mutations ...
|
||||
/// must extended from InputObjectGraphType<T> ...
|
||||
/// </summary>
|
||||
void AddXGraphInputTypes (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Register all GraphQL Queries ...
|
||||
/// Queries in GraphQL used to provide data fetch and retrieve mechanism ...
|
||||
/// extended from ObjectGraphType ...
|
||||
/// </summary>
|
||||
void AddXGraphQueries (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Register all GraphQL Mutations ...
|
||||
/// Mutations is an Action types in GraphQL which data changed through them, like as Add, Update, Delete ...
|
||||
/// must extends from ObjectGraphType ...
|
||||
/// </summary>
|
||||
void AddXGraphMutations (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Register all GraphQL Subscriptions ...
|
||||
/// Subscriptions are listeners to specific events on data in GraphQL ...
|
||||
/// must extended from ObjectGraphType and implements IXBaseRepositoryEvents<TEntity> ...
|
||||
/// </summary>
|
||||
void AddXGraphSubscriptions (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Register all GraphQL Schemas ...
|
||||
/// each available Entity in Database must described to GraphQL with all Graph based stuffs such as :
|
||||
/// Queries, Types and etc, this must done through an Schema class.
|
||||
/// must extended from Schema ...
|
||||
/// </summary>
|
||||
void AddXGraphSchemas (IServiceCollection services);
|
||||
|
||||
/// <summary>
|
||||
/// Add Schemas to GraphQL Builder ...
|
||||
/// here you must add all registered Schemas to GraphQLBuilder ...
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
IGraphQLBuilder AddXGraphTypes (IGraphQLBuilder builder);
|
||||
|
||||
/// <summary>
|
||||
/// Use all Registered Schemas by GraphQL<T> and and GraphQLWebSockets<T> ...
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
void UseXGraph (IApplicationBuilder app);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
public interface IXKeyGenerator<TEntity, TKey>
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
bool IsEmpty (TKey id);
|
||||
Task<TKey> GenerateKey (
|
||||
IXBaseRepository<TEntity, TKey> repository
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
/// <summary>
|
||||
/// this service provide Sequential GUID mechanism for Entity Ids ...
|
||||
/// </summary>
|
||||
public interface IXSequentialGuid {
|
||||
Guid GetCurrentGuid ();
|
||||
Guid Next ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xDataService.Db;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Interfaces {
|
||||
/// <summary>
|
||||
/// Provide Unit Of Works Design Pattern for Db Transactions ...
|
||||
/// Only Used on EFCore ...
|
||||
/// </summary>
|
||||
/// <typeparam name="TDbContext"></typeparam>
|
||||
public interface IXUnitOfWorks<TDbContext> : IDisposable
|
||||
where TDbContext : XDbContext {
|
||||
TDbContext DbContext { get; }
|
||||
DbSet<T> GetDbSet<T, TKey> () where T : XBaseEntity<TKey>;
|
||||
int SaveChanges ();
|
||||
Task<int> SaveChangesAsync ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace xDataService.Models {
|
||||
/// <summary>
|
||||
/// Pass Data on Repository Events ...
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class XBaseEventModel<T> {
|
||||
public T Model { get; set; }
|
||||
|
||||
public XBaseEventModel (T model) {
|
||||
Model = model;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using GraphQL.Types;
|
||||
using xModels.Dtos;
|
||||
|
||||
namespace xDataService.Models {
|
||||
public class XGraphQueryResult<TEntity, TGraph> : ObjectGraphType<XQueryResult<TEntity>>
|
||||
where TGraph : IGraphType {
|
||||
public XGraphQueryResult () {
|
||||
//
|
||||
Name = $"{typeof(TGraph).Name}QueryResult";
|
||||
|
||||
//
|
||||
Field<ListGraphType<TGraph>> (nameof (XQueryResult<TEntity>.Items));
|
||||
Field (x => x.Page);
|
||||
Field (x => x.PageSize);
|
||||
Field (x => x.TotalPages);
|
||||
Field (x => x.TotalFilteredItems);
|
||||
Field (x => x.TotalItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using GraphQL.Types;
|
||||
using xModels.Dtos;
|
||||
|
||||
namespace xDataService.Models {
|
||||
public class XGraphQueryType : InputObjectGraphType<XQuery> {
|
||||
public XGraphQueryType () {
|
||||
//
|
||||
Name = "query";
|
||||
|
||||
//
|
||||
Field (x => x.Filter);
|
||||
Field (x => x.ContainsDetail);
|
||||
Field (x => x.SortBy);
|
||||
Field (x => x.IsAscending);
|
||||
Field (x => x.Page);
|
||||
Field (x => x.PageSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace xDataService.Mongo {
|
||||
public class XAsyncEnumerableAdapter<T> : IAsyncEnumerable<T> {
|
||||
//
|
||||
private readonly IAsyncCursorSource<T> asyncCursorSource;
|
||||
|
||||
//
|
||||
public XAsyncEnumerableAdapter (IAsyncCursorSource<T> asyncCursorSource) {
|
||||
this.asyncCursorSource = asyncCursorSource;
|
||||
}
|
||||
|
||||
//
|
||||
public IAsyncEnumerator<T> GetAsyncEnumerator (CancellationToken cancellationToken) =>
|
||||
new XAsyncEnumeratorAdapter<T> (asyncCursorSource, cancellationToken);
|
||||
}
|
||||
|
||||
public class XAsyncEnumeratorAdapter<T> : IAsyncEnumerator<T> {
|
||||
private readonly IAsyncCursorSource<T> asyncCursorSource;
|
||||
private readonly CancellationToken cancellationToken;
|
||||
private IAsyncCursor<T> asyncCursor;
|
||||
private IEnumerator<T> batchEnumerator;
|
||||
|
||||
public T Current => batchEnumerator.Current;
|
||||
|
||||
public XAsyncEnumeratorAdapter (
|
||||
IAsyncCursorSource<T> asyncCursorSource,
|
||||
CancellationToken cancellationToken
|
||||
) {
|
||||
//
|
||||
this.asyncCursorSource = asyncCursorSource;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> MoveNextAsync () {
|
||||
//
|
||||
if (asyncCursor == null) {
|
||||
asyncCursor = await asyncCursorSource
|
||||
.ToCursorAsync (cancellationToken);
|
||||
}
|
||||
|
||||
//
|
||||
if (batchEnumerator != null &&
|
||||
batchEnumerator.MoveNext ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
if (asyncCursor != null &&
|
||||
await asyncCursor.MoveNextAsync (cancellationToken)) {
|
||||
//
|
||||
batchEnumerator?.Dispose ();
|
||||
batchEnumerator = asyncCursor.Current.GetEnumerator ();
|
||||
|
||||
//
|
||||
return batchEnumerator.MoveNext ();
|
||||
}
|
||||
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync () {
|
||||
//
|
||||
await Task.CompletedTask;
|
||||
asyncCursor?.Dispose ();
|
||||
asyncCursor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace xDataService.Mongo {
|
||||
public class XMongoAsyncCursor<T> : IAsyncCursor<T> {
|
||||
public IEnumerable<T> Current { get; }
|
||||
private IEnumerator<T> CurrentEnumerator { get; }
|
||||
|
||||
public XMongoAsyncCursor (IEnumerable<T> source) {
|
||||
this.Current = source;
|
||||
this.CurrentEnumerator = source.GetEnumerator ();
|
||||
}
|
||||
|
||||
public void Dispose () {
|
||||
CurrentEnumerator.Dispose ();
|
||||
}
|
||||
|
||||
public bool MoveNext (CancellationToken cancellationToken = default) {
|
||||
return CurrentEnumerator.MoveNext ();
|
||||
}
|
||||
|
||||
public Task<bool> MoveNextAsync (CancellationToken cancellationToken = default) {
|
||||
return Task.Run (
|
||||
() => CurrentEnumerator.MoveNext (),
|
||||
cancellationToken : cancellationToken
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Mongo {
|
||||
public class XMongoQueryable<TEntity, TKey> : IMongoQueryable<TEntity>
|
||||
where TEntity : XBaseEntity<TKey> {
|
||||
private readonly IMongoCollection<TEntity> collection;
|
||||
|
||||
public Type ElementType { get; }
|
||||
public IQueryable<TEntity> Items { get; }
|
||||
public Expression Expression { get; }
|
||||
public IQueryProvider Provider { get; }
|
||||
|
||||
public XMongoQueryable (
|
||||
IQueryable<TEntity> items,
|
||||
IMongoCollection<TEntity> collection
|
||||
) {
|
||||
//
|
||||
this.Items = items;
|
||||
this.collection = collection;
|
||||
|
||||
//
|
||||
Provider = items.Provider;
|
||||
Expression = items.Expression;
|
||||
ElementType = items.ElementType;
|
||||
}
|
||||
|
||||
public IEnumerator<TEntity> GetEnumerator () {
|
||||
return Items.GetEnumerator ();
|
||||
}
|
||||
|
||||
public QueryableExecutionModel GetExecutionModel () {
|
||||
return collection.AsQueryable ().GetExecutionModel ();
|
||||
}
|
||||
|
||||
public IAsyncCursor<TEntity> ToCursor (CancellationToken cancellationToken = default) {
|
||||
return new XMongoAsyncCursor<TEntity> (Items);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TEntity>> ToCursorAsync (CancellationToken cancellationToken = default) {
|
||||
return Task.Run (() => new XMongoAsyncCursor<TEntity> (Items) as IAsyncCursor<TEntity>);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator () {
|
||||
return Items.GetEnumerator ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace xDataService.Mongo {
|
||||
public static class XMongoReadPreferenceResolver {
|
||||
public static ReadPreference GetEffectiveReadPreference (
|
||||
IClientSessionHandle session,
|
||||
ReadPreference explicitReadPreference,
|
||||
ReadPreference defaultReadPreference) {
|
||||
if (explicitReadPreference != null) {
|
||||
return explicitReadPreference;
|
||||
}
|
||||
|
||||
if (session.IsInTransaction) {
|
||||
var transactionReadPreference = session.WrappedCoreSession.CurrentTransaction.TransactionOptions.ReadPreference;
|
||||
if (transactionReadPreference != null) {
|
||||
return transactionReadPreference;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultReadPreference ?? ReadPreference.Primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Interfaces;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Providers {
|
||||
public class XGuidKeyGenerator<TEntity> : IXKeyGenerator<TEntity, Guid>
|
||||
where TEntity : XBaseEntity<Guid> {
|
||||
private readonly IXSequentialGuid sequentialGuid;
|
||||
|
||||
public XGuidKeyGenerator (
|
||||
IXSequentialGuid sequentialGuid = null
|
||||
) {
|
||||
this.sequentialGuid = sequentialGuid;
|
||||
}
|
||||
|
||||
public async Task<Guid> GenerateKey (
|
||||
IXBaseRepository<TEntity, Guid> repository
|
||||
) {
|
||||
//
|
||||
await Task.CompletedTask;
|
||||
|
||||
//
|
||||
// Check if provided SequentialGuid ...
|
||||
if (sequentialGuid.IsNull ()) {
|
||||
return Guid.NewGuid ();
|
||||
} else {
|
||||
return sequentialGuid.Next ();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty (Guid id) {
|
||||
//
|
||||
var result = id.IsNull () || id.IsDefaultGuid ();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Extensions;
|
||||
using xDataService.Interfaces;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Providers {
|
||||
public class XIntKeyGenerator<TEntity> : IXKeyGenerator<TEntity, int>
|
||||
where TEntity : XBaseEntity<int> {
|
||||
public async Task<int> GenerateKey (IXBaseRepository<TEntity, int> repository) {
|
||||
//
|
||||
var items = (await repository.GetAllAsync ())
|
||||
.OrderByDescending (nameof (XBaseEntity<int>.Id));
|
||||
var last = items
|
||||
.FirstOrDefault ();
|
||||
|
||||
//
|
||||
var result = last.IsNull () ? 1 : last.Id + 1;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsEmpty (int id) {
|
||||
//
|
||||
var result = id <= 0;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using xDataService.Interfaces;
|
||||
|
||||
namespace xDataService.Helpers {
|
||||
/// <summary>
|
||||
/// this service provide Sequential GUID mechanism for Entity Ids ...
|
||||
/// </summary>
|
||||
public class XSequentialGuid : IXSequentialGuid {
|
||||
private static int[] sqlOrderMap = null;
|
||||
private static int[] SQLORDERMAP {
|
||||
get {
|
||||
if (sqlOrderMap == null) {
|
||||
sqlOrderMap = new int[16] {
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
5,
|
||||
4,
|
||||
7,
|
||||
6,
|
||||
9,
|
||||
8,
|
||||
15,
|
||||
14,
|
||||
13,
|
||||
12,
|
||||
11,
|
||||
10
|
||||
};
|
||||
// 3 - the least significant byte in Guid ByteArray [for SQL Server ORDER BY clause]
|
||||
// 10 - the most significant byte in Guid ByteArray [for SQL Server ORDERY BY clause]
|
||||
}
|
||||
return sqlOrderMap;
|
||||
}
|
||||
}
|
||||
|
||||
private Guid currentGuid;
|
||||
|
||||
public XSequentialGuid () {
|
||||
currentGuid = Guid.NewGuid ();
|
||||
}
|
||||
|
||||
public Guid GetCurrentGuid () {
|
||||
return currentGuid;
|
||||
}
|
||||
|
||||
public Guid Next () {
|
||||
byte[] bytes = currentGuid.ToByteArray ();
|
||||
for (int mapIndex = 0; mapIndex < 16; mapIndex++) {
|
||||
int bytesIndex = SQLORDERMAP[mapIndex];
|
||||
bytes[bytesIndex]++;
|
||||
if (bytes[bytesIndex] != 0) {
|
||||
break; // No need to increment more significant bytes
|
||||
}
|
||||
}
|
||||
currentGuid = new Guid (bytes);
|
||||
return currentGuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Interfaces;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Providers {
|
||||
public class XStringKeyGenerator : IXKeyGenerator<XBaseEntity<string>, string> {
|
||||
private readonly IXSequentialGuid sequentialGuid;
|
||||
|
||||
public XStringKeyGenerator (IXSequentialGuid sequentialGuid = null) {
|
||||
this.sequentialGuid = sequentialGuid;
|
||||
}
|
||||
|
||||
public async Task<string> GenerateKey (IXBaseRepository<XBaseEntity<string>, string> repository) {
|
||||
//
|
||||
await Task.CompletedTask;
|
||||
|
||||
//
|
||||
if (sequentialGuid.IsNull ()) {
|
||||
return Guid.NewGuid ().ToString ();
|
||||
} else {
|
||||
return sequentialGuid.Next ().ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty (string id) {
|
||||
return id.IsNullOrEmpty ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# xDataService
|
||||
|
||||
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.
|
||||
|
||||
this module has following dependencies :
|
||||
|
||||
- xModels
|
||||
- xCommons
|
||||
|
||||
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
|
||||
|
||||
## Maintainer
|
||||
|
||||
Hadi Khazaee asl
|
||||
|
||||
[https://www.saherelm.ir](https://www.saherelm.ir)
|
||||
|
||||
[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com)
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="liget" value="https://nuget.saherelmhub.ir/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
@@ -0,0 +1,65 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- Runtime Definitions -->
|
||||
<PropertyGroup>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>xDashboard.xDataService</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>Hadi Khazaee Asl</Authors>
|
||||
<Company>SaherElm IT Center</Company>
|
||||
<Description>
|
||||
provide base requiremennts for Repository DataBase using and tools for using in xDashboard
|
||||
project.
|
||||
</Description>
|
||||
|
||||
<!-- Icon Definition -->
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Icon Handling -->
|
||||
<ItemGroup>
|
||||
<None Include="../../Resources/Images/favicon.png" Link="icon.png" Pack="true" PackagePath="\icon.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Local Modules -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xDashboard.xModels" Version="1.0.0" />
|
||||
<!-- <ProjectReference Include="../xModels/xModels.csproj" /> -->
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Dependencies -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.13.2" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- GraphQL -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GraphQL" Version="3.0.0.2026" />
|
||||
<PackageReference Include="GraphQL.Server.Ui.Voyager" Version="4.0.1" />
|
||||
<PackageReference Include="GraphQL.SystemTextJson" Version="3.0.0.2026" />
|
||||
<PackageReference Include="GraphQL.Server.Ui.Playground" Version="4.0.1" />
|
||||
<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="4.0.1" />
|
||||
<PackageReference Include="GraphQL.Server.Transports.WebSockets" Version="4.0.1" />
|
||||
<PackageReference Include="GraphQL.Server.Transports.AspNetCore.NewtonsoftJson" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Ef Core -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="3.1.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.14">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.14">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user