CustomEntity ve DapperTransaction
This commit is contained in:
parent
a0fa1b7e2b
commit
fe38608bc7
21 changed files with 409 additions and 71 deletions
|
|
@ -6,6 +6,7 @@ namespace Kurs.Platform.DeveloperKit;
|
||||||
|
|
||||||
public class CustomEntityDto : FullAuditedEntityDto<Guid>
|
public class CustomEntityDto : FullAuditedEntityDto<Guid>
|
||||||
{
|
{
|
||||||
|
public string Menu { get; set; } = string.Empty;
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
public string TableName { get; set; } = string.Empty;
|
public string TableName { get; set; } = string.Empty;
|
||||||
|
|
@ -22,6 +23,7 @@ public class CustomEntityDto : FullAuditedEntityDto<Guid>
|
||||||
|
|
||||||
public class CreateUpdateCustomEntityDto
|
public class CreateUpdateCustomEntityDto
|
||||||
{
|
{
|
||||||
|
public string Menu { get; set; } = string.Empty;
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
public string TableName { get; set; } = string.Empty;
|
public string TableName { get; set; } = string.Empty;
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ public class CustomEntityAppService : CrudAppService<
|
||||||
entity.MigrationId = null;
|
entity.MigrationId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entity.Menu = input.Menu;
|
||||||
entity.Name = input.Name;
|
entity.Name = input.Name;
|
||||||
entity.DisplayName = input.DisplayName;
|
entity.DisplayName = input.DisplayName;
|
||||||
entity.TableName = input.TableName;
|
entity.TableName = input.TableName;
|
||||||
|
|
@ -191,6 +192,7 @@ public class CustomEntityAppService : CrudAppService<
|
||||||
// Entity oluştur
|
// Entity oluştur
|
||||||
var entity = new CustomEntity
|
var entity = new CustomEntity
|
||||||
{
|
{
|
||||||
|
Menu = input.Menu,
|
||||||
Name = input.Name,
|
Name = input.Name,
|
||||||
DisplayName = input.DisplayName,
|
DisplayName = input.DisplayName,
|
||||||
TableName = input.TableName,
|
TableName = input.TableName,
|
||||||
|
|
|
||||||
|
|
@ -10471,6 +10471,12 @@
|
||||||
"en": "Entity Name",
|
"en": "Entity Name",
|
||||||
"tr": "Varlık Adı"
|
"tr": "Varlık Adı"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.DeveloperKit.EntityEditor.MenuName",
|
||||||
|
"en": "Menu Name",
|
||||||
|
"tr": "Menü Adı"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.DeveloperKit.EntityEditor.DisplayName",
|
"key": "App.DeveloperKit.EntityEditor.DisplayName",
|
||||||
|
|
|
||||||
|
|
@ -1419,7 +1419,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "Abp.Identity",
|
"Code": "Abp.Identity",
|
||||||
"DisplayName": "Abp.Identity",
|
"DisplayName": "Abp.Identity",
|
||||||
"Order": 2,
|
"Order": 3,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcConferenceCall",
|
"Icon": "FcConferenceCall",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1519,7 +1519,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.DeveloperKit",
|
"Code": "App.DeveloperKit",
|
||||||
"DisplayName": "App.DeveloperKit",
|
"DisplayName": "App.DeveloperKit",
|
||||||
"Order": 6,
|
"Order": 8,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcAndroidOs",
|
"Icon": "FcAndroidOs",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1599,7 +1599,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Reports.Management",
|
"Code": "App.Reports.Management",
|
||||||
"DisplayName": "App.Reports.Management",
|
"DisplayName": "App.Reports.Management",
|
||||||
"Order": 7,
|
"Order": 6,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcDocument",
|
"Icon": "FcDocument",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1629,7 +1629,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Public",
|
"Code": "App.Public",
|
||||||
"DisplayName": "App.Public",
|
"DisplayName": "App.Public",
|
||||||
"Order": 8,
|
"Order": 7,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcGenealogy",
|
"Icon": "FcGenealogy",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1739,7 +1739,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Definitions",
|
"Code": "App.Definitions",
|
||||||
"DisplayName": "App.Definitions",
|
"DisplayName": "App.Definitions",
|
||||||
"Order": 9,
|
"Order": 2,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcFilingCabinet",
|
"Icon": "FcFilingCabinet",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1749,7 +1749,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Files",
|
"Code": "App.Files",
|
||||||
"DisplayName": "App.Files",
|
"DisplayName": "App.Files",
|
||||||
"Order": 5,
|
"Order": 4,
|
||||||
"Url": "/admin/files",
|
"Url": "/admin/files",
|
||||||
"Icon": "FcFolder",
|
"Icon": "FcFolder",
|
||||||
"RequiredPermissionName": "App.Files",
|
"RequiredPermissionName": "App.Files",
|
||||||
|
|
@ -1825,7 +1825,6 @@
|
||||||
"RequiredPermissionName": "App.Definitions.District",
|
"RequiredPermissionName": "App.Definitions.District",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Definitions",
|
"ParentCode": "App.Definitions",
|
||||||
"Code": "App.Definitions.WorkHour",
|
"Code": "App.Definitions.WorkHour",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;",
|
||||||
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;",
|
||||||
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"Seed": false,
|
"Seed": false,
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=localhost;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=localhost;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;",
|
||||||
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=localhost;Port=5432;Database=Erp;"
|
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=localhost;Port=5432;Database=Erp;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
|
|
|
||||||
|
|
@ -283,8 +283,6 @@ public static class PlatformConsts
|
||||||
public static class AppCodes
|
public static class AppCodes
|
||||||
{
|
{
|
||||||
public const string Home = Prefix.App + ".Home";
|
public const string Home = Prefix.App + ".Home";
|
||||||
|
|
||||||
//Saas
|
|
||||||
public const string Saas = Prefix.App + ".Saas";
|
public const string Saas = Prefix.App + ".Saas";
|
||||||
public const string Branches = Prefix.App + ".Branches";
|
public const string Branches = Prefix.App + ".Branches";
|
||||||
public static class Settings
|
public static class Settings
|
||||||
|
|
@ -328,7 +326,7 @@ public static class PlatformConsts
|
||||||
public static class DynamicServices
|
public static class DynamicServices
|
||||||
{
|
{
|
||||||
public const string DynamicService = Default + ".DynamicServices";
|
public const string DynamicService = Default + ".DynamicServices";
|
||||||
|
|
||||||
public const string Create = DynamicService + ".Create";
|
public const string Create = DynamicService + ".Create";
|
||||||
public const string Edit = DynamicService + ".Edit";
|
public const string Edit = DynamicService + ".Edit";
|
||||||
public const string Delete = DynamicService + ".Delete";
|
public const string Delete = DynamicService + ".Delete";
|
||||||
|
|
@ -338,14 +336,10 @@ public static class PlatformConsts
|
||||||
public const string ViewCode = DynamicService + ".ViewCode";
|
public const string ViewCode = DynamicService + ".ViewCode";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public const string Blog = Prefix.App + ".Blog";
|
public const string Blog = Prefix.App + ".Blog";
|
||||||
public const string Forum = Prefix.App + ".Forum";
|
public const string Forum = Prefix.App + ".Forum";
|
||||||
|
|
||||||
//Administration
|
|
||||||
public const string Administration = Prefix.App + ".Administration";
|
public const string Administration = Prefix.App + ".Administration";
|
||||||
public const string Setting = Prefix.App + ".Setting";
|
public const string Setting = Prefix.App + ".Setting";
|
||||||
|
|
||||||
public static class IdentityManagement
|
public static class IdentityManagement
|
||||||
{
|
{
|
||||||
public const string ClaimTypes = Prefix.App + ".ClaimType";
|
public const string ClaimTypes = Prefix.App + ".ClaimType";
|
||||||
|
|
@ -393,8 +387,6 @@ public static class PlatformConsts
|
||||||
public const string Class = Default + ".Class";
|
public const string Class = Default + ".Class";
|
||||||
public const string Level = Default + ".Level";
|
public const string Level = Default + ".Level";
|
||||||
}
|
}
|
||||||
|
|
||||||
//Hr
|
|
||||||
public static class Hr
|
public static class Hr
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Hr";
|
public const string Default = Prefix.App + ".Hr";
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ public class CustomEntity : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
{
|
{
|
||||||
public virtual Guid? TenantId { get; protected set; }
|
public virtual Guid? TenantId { get; protected set; }
|
||||||
|
|
||||||
|
public string Menu { get; set; } = string.Empty;
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string DisplayName { get; set; } = string.Empty;
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
public string TableName { get; set; } = string.Empty;
|
public string TableName { get; set; } = string.Empty;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Data.Common;
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp.Threading;
|
using Volo.Abp.Threading;
|
||||||
|
|
@ -9,6 +10,7 @@ namespace Kurs.Platform.Domain.DynamicData;
|
||||||
public class DapperTransactionApi : ITransactionApi, ISupportsRollback
|
public class DapperTransactionApi : ITransactionApi, ISupportsRollback
|
||||||
{
|
{
|
||||||
public DbTransaction DbTransaction { get; }
|
public DbTransaction DbTransaction { get; }
|
||||||
|
private bool _isCompleted;
|
||||||
|
|
||||||
protected ICancellationTokenProvider CancellationTokenProvider { get; }
|
protected ICancellationTokenProvider CancellationTokenProvider { get; }
|
||||||
|
|
||||||
|
|
@ -18,20 +20,82 @@ public class DapperTransactionApi : ITransactionApi, ISupportsRollback
|
||||||
{
|
{
|
||||||
DbTransaction = dbTransaction;
|
DbTransaction = dbTransaction;
|
||||||
CancellationTokenProvider = cancellationTokenProvider;
|
CancellationTokenProvider = cancellationTokenProvider;
|
||||||
|
_isCompleted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CommitAsync(CancellationToken cancellationToken = default)
|
public async Task CommitAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await DbTransaction.CommitAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
|
// Check if transaction is still active
|
||||||
|
if (_isCompleted)
|
||||||
|
{
|
||||||
|
return; // Already completed, nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if connection is still open and transaction is not disposed
|
||||||
|
if (DbTransaction?.Connection == null || DbTransaction.Connection.State != ConnectionState.Open)
|
||||||
|
{
|
||||||
|
_isCompleted = true;
|
||||||
|
return; // Connection closed or transaction disposed
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await DbTransaction.CommitAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
|
||||||
|
_isCompleted = true;
|
||||||
|
}
|
||||||
|
catch (System.InvalidOperationException)
|
||||||
|
{
|
||||||
|
// Transaction already completed or disposed
|
||||||
|
_isCompleted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
DbTransaction.Dispose();
|
if (!_isCompleted && DbTransaction?.Connection != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If not completed, rollback before disposing
|
||||||
|
DbTransaction?.Rollback();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore rollback errors during disposal
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isCompleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DbTransaction?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RollbackAsync(CancellationToken cancellationToken)
|
public async Task RollbackAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await DbTransaction.RollbackAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
|
// Check if transaction is still active
|
||||||
|
if (_isCompleted)
|
||||||
|
{
|
||||||
|
return; // Already completed, nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if connection is still open and transaction is not disposed
|
||||||
|
if (DbTransaction?.Connection == null || DbTransaction.Connection.State != ConnectionState.Open)
|
||||||
|
{
|
||||||
|
_isCompleted = true;
|
||||||
|
return; // Connection closed or transaction disposed
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await DbTransaction.RollbackAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
|
||||||
|
_isCompleted = true;
|
||||||
|
}
|
||||||
|
catch (System.InvalidOperationException)
|
||||||
|
{
|
||||||
|
// Transaction already completed or disposed
|
||||||
|
_isCompleted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
||||||
{
|
{
|
||||||
private readonly IUnitOfWorkManager unitOfWorkManager;
|
private readonly IUnitOfWorkManager unitOfWorkManager;
|
||||||
private readonly ICancellationTokenProvider cancellationTokenProvider;
|
private readonly ICancellationTokenProvider cancellationTokenProvider;
|
||||||
private Dictionary<string, DbTransaction> transactions;
|
private readonly Dictionary<string, DbTransaction> transactions;
|
||||||
private Dictionary<string, SqlConnection> connections;
|
private readonly Dictionary<string, SqlConnection> connections;
|
||||||
|
private readonly HashSet<string> registeredTransactions; // Track registered transactions
|
||||||
|
private readonly object _lock = new object();
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public MsDynamicDataRepository(
|
public MsDynamicDataRepository(
|
||||||
|
|
@ -27,41 +29,159 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
||||||
{
|
{
|
||||||
this.unitOfWorkManager = unitOfWorkManager;
|
this.unitOfWorkManager = unitOfWorkManager;
|
||||||
this.cancellationTokenProvider = cancellationTokenProvider;
|
this.cancellationTokenProvider = cancellationTokenProvider;
|
||||||
transactions = [];
|
transactions = new Dictionary<string, DbTransaction>();
|
||||||
connections = [];
|
connections = new Dictionary<string, SqlConnection>();
|
||||||
|
registeredTransactions = new HashSet<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<DbTransaction> GetOrCreateTransactionAsync(SqlConnection con)
|
private async Task<DbTransaction> GetOrCreateTransactionAsync(SqlConnection con)
|
||||||
{
|
{
|
||||||
var key = $"Dapper_{con.ConnectionString}";
|
var key = $"Dapper_{con.ConnectionString}";
|
||||||
var transaction = transactions.GetOrDefault(key);
|
|
||||||
if (transaction == null || transaction.Connection == null)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
transaction = await con.BeginTransactionAsync();
|
// Check if we have a valid transaction for this connection
|
||||||
unitOfWorkManager.Current.AddTransactionApi(key, new DapperTransactionApi(transaction, cancellationTokenProvider));
|
if (transactions.TryGetValue(key, out var transaction))
|
||||||
transactions[key] = transaction;
|
{
|
||||||
|
// Validate transaction is still usable
|
||||||
|
if (transaction?.Connection != null &&
|
||||||
|
transaction.Connection == con &&
|
||||||
|
transaction.Connection.State == ConnectionState.Open)
|
||||||
|
{
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid transaction, remove it
|
||||||
|
try { transaction?.Dispose(); } catch { }
|
||||||
|
transactions.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure connection is open
|
||||||
|
if (con.State != ConnectionState.Open)
|
||||||
|
{
|
||||||
|
await con.OpenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new transaction
|
||||||
|
var newTransaction = await con.BeginTransactionAsync();
|
||||||
|
bool shouldRegister = false;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
transactions[key] = newTransaction;
|
||||||
|
|
||||||
|
// Only register with UnitOfWork once per transaction key
|
||||||
|
if (!registeredTransactions.Contains(key))
|
||||||
|
{
|
||||||
|
registeredTransactions.Add(key);
|
||||||
|
shouldRegister = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register with UnitOfWork if available and not already registered
|
||||||
|
if (shouldRegister && unitOfWorkManager.Current != null)
|
||||||
|
{
|
||||||
|
unitOfWorkManager.Current.AddTransactionApi(key, new DapperTransactionApi(newTransaction, cancellationTokenProvider));
|
||||||
|
|
||||||
unitOfWorkManager.Current.OnCompleted(() =>
|
unitOfWorkManager.Current.OnCompleted(() =>
|
||||||
{
|
{
|
||||||
transaction = null;
|
lock (_lock)
|
||||||
|
{
|
||||||
|
transactions.Remove(key);
|
||||||
|
registeredTransactions.Remove(key);
|
||||||
|
}
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return transaction;
|
|
||||||
|
return newTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SqlConnection> GetOrCreateConnectionAsync(string cs)
|
private async Task<SqlConnection> GetOrCreateConnectionAsync(string cs)
|
||||||
{
|
{
|
||||||
|
SqlConnection connection;
|
||||||
var key = $"Dapper_{cs}";
|
var key = $"Dapper_{cs}";
|
||||||
var connection = connections.GetOrDefault(key);
|
|
||||||
if (connection == null)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
connection = new SqlConnection(cs);
|
// Check if we have an existing connection
|
||||||
connections[key] = connection; // Use indexer instead of Add to avoid duplicate key exception
|
if (connections.TryGetValue(key, out connection))
|
||||||
|
{
|
||||||
|
// Connection exists, check its state
|
||||||
|
if (connection.State == ConnectionState.Open)
|
||||||
|
{
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection is not open, will handle outside lock
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create new connection
|
||||||
|
connection = new SqlConnection(cs);
|
||||||
|
connections[key] = connection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (connection.State != ConnectionState.Open)
|
|
||||||
|
// Handle connection state outside of lock
|
||||||
|
try
|
||||||
{
|
{
|
||||||
|
if (connection.State == ConnectionState.Broken)
|
||||||
|
{
|
||||||
|
connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection.State == ConnectionState.Closed)
|
||||||
|
{
|
||||||
|
await connection.OpenAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If connection failed, create a new one
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
connections.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection = new SqlConnection(cs);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
connections[key] = connection;
|
||||||
|
}
|
||||||
await connection.OpenAsync();
|
await connection.OpenAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register cleanup on UnitOfWork completion (only once)
|
||||||
|
if (unitOfWorkManager.Current != null)
|
||||||
|
{
|
||||||
|
unitOfWorkManager.Current.OnCompleted(async () =>
|
||||||
|
{
|
||||||
|
SqlConnection conn = null;
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (connections.TryGetValue(key, out conn))
|
||||||
|
{
|
||||||
|
connections.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (conn.State != ConnectionState.Closed)
|
||||||
|
{
|
||||||
|
await conn.CloseAsync();
|
||||||
|
}
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,16 +255,42 @@ public class MsDynamicDataRepository : IDynamicDataRepository, IScopedDependency
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
foreach (var connection in connections.Values)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (connection != null)
|
// Dispose transactions first
|
||||||
|
foreach (var transaction in transactions.Values)
|
||||||
{
|
{
|
||||||
if (connection.State == ConnectionState.Open)
|
try
|
||||||
{
|
{
|
||||||
connection.Close();
|
transaction?.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore disposal errors
|
||||||
}
|
}
|
||||||
connection.Dispose();
|
|
||||||
}
|
}
|
||||||
|
transactions.Clear();
|
||||||
|
|
||||||
|
// Then dispose connections
|
||||||
|
foreach (var connection in connections.Values)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (connection != null)
|
||||||
|
{
|
||||||
|
if (connection.State == ConnectionState.Open)
|
||||||
|
{
|
||||||
|
connection.Close();
|
||||||
|
}
|
||||||
|
connection.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore disposal errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connections.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Kurs.Platform.Migrations
|
namespace Kurs.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20251105142841_Initial")]
|
[Migration("20251106171552_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -2951,6 +2951,9 @@ namespace Kurs.Platform.Migrations
|
||||||
.HasColumnType("uniqueidentifier")
|
.HasColumnType("uniqueidentifier")
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
|
b.Property<string>("Menu")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<Guid?>("MigrationId")
|
b.Property<Guid?>("MigrationId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
|
@ -2132,6 +2132,7 @@ namespace Kurs.Platform.Migrations
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
Menu = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
DisplayName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
DisplayName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
TableName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
TableName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
|
@ -2948,6 +2948,9 @@ namespace Kurs.Platform.Migrations
|
||||||
.HasColumnType("uniqueidentifier")
|
.HasColumnType("uniqueidentifier")
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
|
b.Property<string>("Menu")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<Guid?>("MigrationId")
|
b.Property<Guid?>("MigrationId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"BaseDomain": "sozsoft.com"
|
"BaseDomain": "sozsoft.com"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;",
|
||||||
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"BaseDomain": "sozsoft.com"
|
"BaseDomain": "sozsoft.com"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=sql;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;",
|
||||||
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Erp;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"Version": "1.0.1"
|
"Version": "1.0.1"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=localhost;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=localhost;Database=Erp;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;",
|
||||||
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=localhost;Port=5432;Database=Erp;"
|
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=localhost;Port=5432;Database=Erp;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,11 @@ import { Formik, Form, Field, FieldProps, FieldArray } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { FormItem, Input, Select, Checkbox, FormContainer, Button } from '@/components/ui'
|
import { FormItem, Input, Select, Checkbox, FormContainer, Button } from '@/components/ui'
|
||||||
import { SelectBoxOption } from '@/shared/types'
|
import { SelectBoxOption } from '@/shared/types'
|
||||||
|
import { MenuPrefixEnum, menuPrefixValues, toPrefix } from '@/types/menu'
|
||||||
|
|
||||||
// Validation schema
|
// Validation schema
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
|
menu: Yup.string().required('Menu is required'),
|
||||||
name: Yup.string().required('Entity name is required'),
|
name: Yup.string().required('Entity name is required'),
|
||||||
displayName: Yup.string().required('Display name is required'),
|
displayName: Yup.string().required('Display name is required'),
|
||||||
tableName: Yup.string().required('Table name is required'),
|
tableName: Yup.string().required('Table name is required'),
|
||||||
|
|
@ -55,6 +57,7 @@ const EntityEditor: React.FC = () => {
|
||||||
|
|
||||||
// Initial values for Formik
|
// Initial values for Formik
|
||||||
const [initialValues, setInitialValues] = useState({
|
const [initialValues, setInitialValues] = useState({
|
||||||
|
menu: '',
|
||||||
name: '',
|
name: '',
|
||||||
displayName: '',
|
displayName: '',
|
||||||
tableName: '',
|
tableName: '',
|
||||||
|
|
@ -87,6 +90,7 @@ const EntityEditor: React.FC = () => {
|
||||||
.map((f, idx) => ({ ...f, displayOrder: f.displayOrder ?? idx + 1 }))
|
.map((f, idx) => ({ ...f, displayOrder: f.displayOrder ?? idx + 1 }))
|
||||||
|
|
||||||
setInitialValues({
|
setInitialValues({
|
||||||
|
menu: entity.menu,
|
||||||
name: entity.name,
|
name: entity.name,
|
||||||
displayName: entity.displayName,
|
displayName: entity.displayName,
|
||||||
tableName: entity.tableName,
|
tableName: entity.tableName,
|
||||||
|
|
@ -121,6 +125,7 @@ const EntityEditor: React.FC = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const entityData = {
|
const entityData = {
|
||||||
|
menu: values.menu.trim(),
|
||||||
name: values.name.trim(),
|
name: values.name.trim(),
|
||||||
displayName: values.displayName.trim(),
|
displayName: values.displayName.trim(),
|
||||||
tableName: values.tableName.trim(),
|
tableName: values.tableName.trim(),
|
||||||
|
|
@ -146,6 +151,18 @@ const EntityEditor: React.FC = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to generate table name
|
||||||
|
const generateTableName = (menuValue: string, entityName: string): string => {
|
||||||
|
if (!menuValue || !entityName) return ''
|
||||||
|
try {
|
||||||
|
const menuPrefix = toPrefix(menuValue as MenuPrefixEnum)
|
||||||
|
return `${menuPrefix}_D_${entityName}`
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating table name:', error)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fieldTypes = [
|
const fieldTypes = [
|
||||||
{ value: 'string', label: 'String' },
|
{ value: 'string', label: 'String' },
|
||||||
{ value: 'number', label: 'Number' },
|
{ value: 'number', label: 'Number' },
|
||||||
|
|
@ -236,13 +253,15 @@ const EntityEditor: React.FC = () => {
|
||||||
Migration Applied - Changes Will Require New Migration
|
Migration Applied - Changes Will Require New Migration
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs text-yellow-700">
|
<p className="text-xs text-yellow-700">
|
||||||
This entity has been migrated to the database. Any structural changes you make will reset the migration status to "pending", and you'll need to generate and apply a new migration to update the database schema.
|
This entity has been migrated to the database. Any structural changes you make
|
||||||
|
will reset the migration status to "pending", and you'll need to generate and
|
||||||
|
apply a new migration to update the database schema.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Basic Entity Information */}
|
{/* Basic Entity Information */}
|
||||||
<div className="space-y-4 col-span-1">
|
<div className="space-y-4 col-span-1">
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-slate-200 p-3">
|
<div className="bg-white rounded-lg shadow-sm border border-slate-200 p-3">
|
||||||
|
|
@ -254,6 +273,33 @@ const EntityEditor: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormContainer size="sm">
|
<FormContainer size="sm">
|
||||||
|
<FormItem
|
||||||
|
label={translate('::App.DeveloperKit.EntityEditor.MenuName')}
|
||||||
|
invalid={!!(errors.menu && touched.menu)}
|
||||||
|
errorMessage={errors.menu as string}
|
||||||
|
>
|
||||||
|
<Field name="menu">
|
||||||
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||||
|
<Select
|
||||||
|
field={field}
|
||||||
|
form={form}
|
||||||
|
options={menuPrefixValues}
|
||||||
|
isClearable={true}
|
||||||
|
value={menuPrefixValues.filter((option) => option.value === values.menu)}
|
||||||
|
onChange={(option) => {
|
||||||
|
const newMenuValue = option?.value || ''
|
||||||
|
form.setFieldValue(field.name, newMenuValue)
|
||||||
|
// Update table name when menu changes
|
||||||
|
if (values.name && newMenuValue) {
|
||||||
|
const newTableName = generateTableName(newMenuValue, values.name)
|
||||||
|
form.setFieldValue('tableName', newTableName)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.DeveloperKit.EntityEditor.EntityName')}
|
label={translate('::App.DeveloperKit.EntityEditor.EntityName')}
|
||||||
invalid={!!(errors.name && touched.name)}
|
invalid={!!(errors.name && touched.name)}
|
||||||
|
|
@ -265,11 +311,15 @@ const EntityEditor: React.FC = () => {
|
||||||
{...field}
|
{...field}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
field.onBlur(e)
|
field.onBlur(e)
|
||||||
if (!values.tableName) {
|
const entityName = e.target.value.trim()
|
||||||
setFieldValue('tableName', values.name + 's')
|
// Update table name based on menu prefix and entity name
|
||||||
|
if (entityName && values.menu) {
|
||||||
|
const newTableName = generateTableName(values.menu, entityName)
|
||||||
|
setFieldValue('tableName', newTableName)
|
||||||
}
|
}
|
||||||
if (!values.displayName) {
|
// Update display name if empty
|
||||||
setFieldValue('displayName', values.name)
|
if (entityName && !values.displayName) {
|
||||||
|
setFieldValue('displayName', entityName)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placeholder="e.g., Product, User, Order"
|
placeholder="e.g., Product, User, Order"
|
||||||
|
|
@ -279,18 +329,6 @@ const EntityEditor: React.FC = () => {
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.DeveloperKit.EntityEditor.DisplayName')}
|
|
||||||
invalid={!!(errors.displayName && touched.displayName)}
|
|
||||||
errorMessage={errors.displayName as string}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="displayName"
|
|
||||||
component={Input}
|
|
||||||
placeholder="e.g., Product, User, Order"
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.DeveloperKit.EntityEditor.TableName')}
|
label={translate('::App.DeveloperKit.EntityEditor.TableName')}
|
||||||
invalid={!!(errors.tableName && touched.tableName)}
|
invalid={!!(errors.tableName && touched.tableName)}
|
||||||
|
|
@ -299,8 +337,31 @@ const EntityEditor: React.FC = () => {
|
||||||
<Field
|
<Field
|
||||||
name="tableName"
|
name="tableName"
|
||||||
component={Input}
|
component={Input}
|
||||||
placeholder="e.g., Products, Users, Orders"
|
placeholder="e.g., Adm_D_Product, Net_D_User"
|
||||||
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
|
<p className="text-xs text-slate-500 mt-1">
|
||||||
|
Format:{' '}
|
||||||
|
{values.menu
|
||||||
|
? `${toPrefix(values.menu as MenuPrefixEnum)}_D_`
|
||||||
|
: '[Prefix]_D_'}
|
||||||
|
EntityName
|
||||||
|
</p>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem
|
||||||
|
label={translate('::App.DeveloperKit.EntityEditor.DisplayName')}
|
||||||
|
invalid={!!(errors.displayName && touched.displayName)}
|
||||||
|
errorMessage={errors.displayName as string}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
name="displayName"
|
||||||
|
component={Input}
|
||||||
|
placeholder="Display name for UI (e.g., Product, User)"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-slate-500 mt-1">
|
||||||
|
User-friendly name shown in the interface
|
||||||
|
</p>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
|
|
@ -313,7 +374,7 @@ const EntityEditor: React.FC = () => {
|
||||||
component={Input}
|
component={Input}
|
||||||
placeholder="Brief description of this entity"
|
placeholder="Brief description of this entity"
|
||||||
textArea={true}
|
textArea={true}
|
||||||
rows={3}
|
rows={2}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
|
@ -324,7 +385,8 @@ const EntityEditor: React.FC = () => {
|
||||||
<FormItem label="Full Audited Entity">
|
<FormItem label="Full Audited Entity">
|
||||||
<Field name="isFullAuditedEntity" component={Checkbox} />
|
<Field name="isFullAuditedEntity" component={Checkbox} />
|
||||||
<p className="text-xs text-slate-500 mt-1">
|
<p className="text-xs text-slate-500 mt-1">
|
||||||
Includes CreationTime, CreatorId, LastModificationTime, LastModifierId, IsDeleted, DeleterId, DeletionTime
|
Includes CreationTime, CreatorId, LastModificationTime, LastModifierId,
|
||||||
|
IsDeleted, DeleterId, DeletionTime
|
||||||
</p>
|
</p>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
|
@ -378,7 +440,7 @@ const EntityEditor: React.FC = () => {
|
||||||
disabled:cursor-not-allowed
|
disabled:cursor-not-allowed
|
||||||
disabled:hover:from-gray-400 disabled:hover:to-gray-500
|
disabled:hover:from-gray-400 disabled:hover:to-gray-500
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<FaPlus className="w-2.5 h-2.5" />
|
<FaPlus className="w-2.5 h-2.5" />
|
||||||
{translate('::App.DeveloperKit.EntityEditor.AddField')}
|
{translate('::App.DeveloperKit.EntityEditor.AddField')}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export interface CustomEntity {
|
export interface CustomEntity {
|
||||||
id: string;
|
id: string;
|
||||||
|
menu: string;
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
tableName: string;
|
tableName: string;
|
||||||
|
|
@ -43,6 +44,7 @@ export interface CreateUpdateCustomEntityFieldDto {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateUpdateCustomEntityDto {
|
export interface CreateUpdateCustomEntityDto {
|
||||||
|
menu: string;
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
tableName: string;
|
tableName: string;
|
||||||
|
|
|
||||||
55
ui/src/types/menu.ts
Normal file
55
ui/src/types/menu.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { enumToList } from "@/utils/enumUtils";
|
||||||
|
|
||||||
|
export enum MenuPrefixEnum {
|
||||||
|
Platform = "Platform",
|
||||||
|
Saas = "Saas",
|
||||||
|
Administration = "Administration",
|
||||||
|
Intranet = "Intranet",
|
||||||
|
Participant = "Participant",
|
||||||
|
Coordinator = "Coordinator",
|
||||||
|
Crm = "Crm",
|
||||||
|
SupplyChain = "SupplyChain",
|
||||||
|
Maintenance = "Maintenance",
|
||||||
|
Warehouse = "Warehouse",
|
||||||
|
Project = "Project",
|
||||||
|
Hr = "Hr",
|
||||||
|
Mrp = "Mrp",
|
||||||
|
Accounting = "Accounting"
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toPrefix(menu: MenuPrefixEnum): string {
|
||||||
|
switch (menu) {
|
||||||
|
case MenuPrefixEnum.Platform:
|
||||||
|
return "Plat";
|
||||||
|
case MenuPrefixEnum.Saas:
|
||||||
|
return "Sas";
|
||||||
|
case MenuPrefixEnum.Administration:
|
||||||
|
return "Adm";
|
||||||
|
case MenuPrefixEnum.Intranet:
|
||||||
|
return "Net";
|
||||||
|
case MenuPrefixEnum.Participant:
|
||||||
|
return "Prt";
|
||||||
|
case MenuPrefixEnum.Coordinator:
|
||||||
|
return "Crd";
|
||||||
|
case MenuPrefixEnum.Crm:
|
||||||
|
return "Crm";
|
||||||
|
case MenuPrefixEnum.SupplyChain:
|
||||||
|
return "Scp";
|
||||||
|
case MenuPrefixEnum.Maintenance:
|
||||||
|
return "Mnt";
|
||||||
|
case MenuPrefixEnum.Warehouse:
|
||||||
|
return "Wh";
|
||||||
|
case MenuPrefixEnum.Project:
|
||||||
|
return "Prj";
|
||||||
|
case MenuPrefixEnum.Hr:
|
||||||
|
return "Hr";
|
||||||
|
case MenuPrefixEnum.Mrp:
|
||||||
|
return "Mrp";
|
||||||
|
case MenuPrefixEnum.Accounting:
|
||||||
|
return "Acc";
|
||||||
|
default:
|
||||||
|
throw new Error(`Unhandled menu prefix: ${menu}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const menuPrefixValues = enumToList<string>(MenuPrefixEnum)
|
||||||
|
|
@ -279,7 +279,7 @@ function TenantConnectionString({
|
||||||
'value',
|
'value',
|
||||||
'Server=sql;Database=' +
|
'Server=sql;Database=' +
|
||||||
name +
|
name +
|
||||||
';User Id=sa;password=@Password;Trusted_Connection=False;TrustServerCertificate=True;',
|
';User Id=sa;password=@Password;Trusted_Connection=False;TrustServerCertificate=True;Connection Timeout=60;MultipleActiveResultSets=true;',
|
||||||
)
|
)
|
||||||
else if (option?.value == 2)
|
else if (option?.value == 2)
|
||||||
//MsSql
|
//MsSql
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue