ListForm AppService performans güçlendirmesi

This commit is contained in:
Sedat Öztürk 2026-02-04 21:51:05 +03:00
parent 8014d9df34
commit 1f97581874
3 changed files with 138 additions and 110 deletions

View file

@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;
using static Erp.Platform.PlatformConsts;
namespace Erp.Platform.ListForms;
@ -46,42 +47,35 @@ public class ListFormCustomizationAppService : PlatformAppService
throw new BadHttpRequestException("Type UI veya Grid olmalıdır");
}
var query = await repository.GetQueryableAsync();
var queryable = await repository.GetQueryableAsync();
var itemsUser = await query
.Where(a =>
// Tek query ile tüm ilgili kayıtları çek (3 query yerine 1)
var allItems = await AsyncExecuter.ToListAsync(
queryable.Where(a =>
a.ListFormCode == listFormCode &&
a.UserId == CurrentUser.UserName &&
a.RoleId == null &&
a.CustomizationType == type)
a.CustomizationType == type &&
(
// User customizations
(a.UserId == CurrentUser.UserName && a.RoleId == null) ||
// Role customizations
(a.UserId == null && CurrentUser.Roles.Contains(a.RoleId)) ||
// Global customizations
(a.UserId == null && a.RoleId == null)
)
)
.WhereIf(!filterName.IsNullOrWhiteSpace(), a => a.FilterName == filterName)
.ToListAsync();
var itemsRole = await query
.Where(a =>
a.ListFormCode == listFormCode &&
a.UserId == null &&
CurrentUser.Roles.Contains(a.RoleId) &&
a.CustomizationType == type)
.WhereIf(!filterName.IsNullOrWhiteSpace(), a => a.FilterName == filterName)
.ToListAsync();
var itemsGlobal = await query
.Where(a =>
a.ListFormCode == listFormCode &&
a.UserId == null &&
a.RoleId == null &&
a.CustomizationType == type)
.WhereIf(!filterName.IsNullOrWhiteSpace(), a => a.FilterName == filterName)
.ToListAsync();
);
var items = new List<ListFormCustomization>();
items.AddRange(itemsUser);
items.AddRange(itemsRole);
items.AddRange(itemsGlobal);
// Öncelik sırasına göre sırala: User > Role > Global
var items = allItems
.OrderByDescending(a => a.UserId != null ? 3 : (a.RoleId != null ? 2 : 1))
.ToList();
return ObjectMapper.Map<List<ListFormCustomization>, List<ListFormCustomizationForUserDto>>(items);
}
//Bu hem update, hem create yapıyor
[UnitOfWork]
public async Task<ListFormCustomizationForUserDto> CreateAsync(CreateUpdateListFormCustomizationForUserDto input)
{
await CheckAccessAsync(input.ListFormCode);
@ -94,12 +88,15 @@ public class ListFormCustomizationAppService : PlatformAppService
throw new BadHttpRequestException("Filter data boş olamaz");
}
var item = await repository.FirstOrDefaultAsync(a =>
a.ListFormCode == input.ListFormCode &&
a.UserId == CurrentUser.UserName &&
a.RoleId == null &&
a.FilterName == input.FilterName &&
a.CustomizationType == input.CustomizationType);
var queryable = await repository.GetQueryableAsync();
var item = await AsyncExecuter.FirstOrDefaultAsync(
queryable.Where(a =>
a.ListFormCode == input.ListFormCode &&
a.UserId == CurrentUser.UserName &&
a.RoleId == null &&
a.FilterName == input.FilterName &&
a.CustomizationType == input.CustomizationType)
);
if (item == null)
{
@ -111,13 +108,13 @@ public class ListFormCustomizationAppService : PlatformAppService
ListFormCode = input.ListFormCode,
UserId = CurrentUser.UserName
};
await repository.InsertAsync(item);
await repository.InsertAsync(item, autoSave: true);
}
else
{
item.FilterName = input.FilterName;
item.CustomizationData = input.CustomizationData;
await repository.UpdateAsync(item);
await repository.UpdateAsync(item, autoSave: true);
}
return ObjectMapper.Map<ListFormCustomization, ListFormCustomizationForUserDto>(item);
@ -125,11 +122,14 @@ public class ListFormCustomizationAppService : PlatformAppService
public async Task DeleteAsync(Guid id)
{
var item = await repository.FirstOrDefaultAsync(a =>
a.Id == id &&
a.UserId == CurrentUser.UserName &&
a.RoleId == null
var queryable = await repository.GetQueryableAsync();
var item = await AsyncExecuter.FirstOrDefaultAsync(
queryable.Where(a =>
a.Id == id &&
a.UserId == CurrentUser.UserName &&
a.RoleId == null)
);
if (item == null)
{
throw new EntityNotFoundException("Item not found");
@ -142,8 +142,6 @@ public class ListFormCustomizationAppService : PlatformAppService
await CheckAccessAsync(item.ListFormCode);
await repository.DeleteAsync(item);
await repository.DeleteAsync(item, autoSave: true);
}
}

View file

@ -15,6 +15,7 @@ using Volo.Abp;
using Volo.Abp.Content;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;
using static Erp.Platform.PlatformConsts;
namespace Erp.Platform.ListForms.ImportManager;
@ -89,7 +90,8 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
public async Task<ListFormsImportDto> GetAsync(Guid id)
{
var session = await _importSessionRepository.FirstOrDefaultAsync(a => a.Id == id)
var queryable = await _importSessionRepository.GetQueryableAsync();
var session = await AsyncExecuter.FirstOrDefaultAsync(queryable.Where(a => a.Id == id))
?? throw new EntityNotFoundException(typeof(ListFormImport), id);
if (!await _authManager.CanAccess(session.ListFormCode, AuthorizationTypeEnum.Import))
@ -103,10 +105,14 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
if (!await _authManager.CanAccess(listFormCode, AuthorizationTypeEnum.Import))
throw new Volo.Abp.UserFriendlyException(L[AppErrorCodes.NoAuth]);
var sessions = await _importSessionRepository.GetListAsync(x => x.ListFormCode == listFormCode);
var queryable = await _importSessionRepository.GetQueryableAsync();
var sessions = await AsyncExecuter.ToListAsync(
queryable
.Where(x => x.ListFormCode == listFormCode)
.OrderByDescending(x => x.CreationTime)
);
var ordered = sessions.OrderByDescending(x => x.CreationTime).ToList();
return ObjectMapper.Map<List<ListFormImport>, List<ListFormsImportDto>>(ordered);
return ObjectMapper.Map<List<ListFormImport>, List<ListFormsImportDto>>(sessions);
}
@ -116,7 +122,8 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
if (!await _authManager.CanAccess(input.ListFormCode, AuthorizationTypeEnum.Import))
throw new Volo.Abp.UserFriendlyException(L[AppErrorCodes.NoAuth]);
var session = await _importSessionRepository.FirstOrDefaultAsync(a => a.Id == id)
var queryable = await _importSessionRepository.GetQueryableAsync();
var session = await AsyncExecuter.FirstOrDefaultAsync(queryable.Where(a => a.Id == id))
?? throw new EntityNotFoundException(typeof(ListFormImport), id);
if (!string.IsNullOrEmpty(input.Status)) session.Status = input.Status;
@ -126,9 +133,11 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
return ObjectMapper.Map<ListFormImport, ListFormsImportDto>(session);
}
[UnitOfWork]
public async Task<ListFormImportExecuteDto> ExecuteAsync([FromBody] ExecuteImportRequest request)
{
var session = await _importSessionRepository.FirstOrDefaultAsync(a => a.Id == request.SessionId)
var queryable = await _importSessionRepository.GetQueryableAsync();
var session = await AsyncExecuter.FirstOrDefaultAsync(queryable.Where(a => a.Id == request.SessionId))
?? throw new UserFriendlyException("Import session not found.");
// Izin logic process
@ -159,6 +168,10 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
execute.Progress = 20;
await _importSessionExecuteRepository.UpdateAsync(execute, autoSave: true);
// Batch processing için - daha az update
const int updateInterval = 50; // Her 50 satırda bir update
var lastUpdateIndex = 0;
// Process each row individually
for (int i = 0; i < request.SelectedRowsData.Count; i++)
{
@ -177,17 +190,20 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
// If database insert fails, count as error
errorCount++;
// Log the error if needed
Logger.LogWarning("Error inserting row {0} during import for session {1}: {2}", i, session.Id, ex.Message);
Logger.LogWarning("Error inserting row {RowIndex} during import for session {SessionId}: {ErrorMessage}", i, session.Id, ex.Message);
}
// Update progress
var progressPercentage = 20 + (int)((double)(i + 1) / processedCount * 70); // Progress from 20% to 90%
execute.Progress = progressPercentage;
// Update progress every 10 rows or on last row to avoid too many database updates
if ((i + 1) % 10 == 0 || i == processedCount - 1)
// Update progress - optimizasyon: daha az update
if ((i + 1 - lastUpdateIndex >= updateInterval) || i == processedCount - 1)
{
await _importSessionExecuteRepository.UpdateAsync(execute, autoSave: true);
var progressPercentage = 20 + (int)((double)(i + 1) / processedCount * 70); // Progress from 20% to 90%
execute.Progress = progressPercentage;
execute.ExecRows = i + 1;
execute.ValidRows = validCount;
execute.ErrorRows = errorCount;
await _importSessionExecuteRepository.UpdateAsync(execute, autoSave: false);
lastUpdateIndex = i + 1;
}
}
}
@ -216,22 +232,27 @@ public class ListFormImportAppService : PlatformAppService, IImportAppService
public async Task<List<ListFormImportExecuteDto>> GetListExecutesAsync(Guid sessionId)
{
var sessions = await _importSessionExecuteRepository.GetListAsync(x => x.ImportId == sessionId);
var queryable = await _importSessionExecuteRepository.GetQueryableAsync();
var sessions = await AsyncExecuter.ToListAsync(
queryable
.Where(x => x.ImportId == sessionId)
.OrderByDescending(x => x.CreationTime)
);
var ordered = sessions.OrderByDescending(x => x.CreationTime).ToList();
return ObjectMapper.Map<List<ListFormImportExecute>, List<ListFormImportExecuteDto>>(ordered);
return ObjectMapper.Map<List<ListFormImportExecute>, List<ListFormImportExecuteDto>>(sessions);
}
public async Task DeleteAsync(Guid id)
{
var session = await _importSessionRepository.FirstOrDefaultAsync(a => a.Id == id)
var queryable = await _importSessionRepository.GetQueryableAsync();
var session = await AsyncExecuter.FirstOrDefaultAsync(queryable.Where(a => a.Id == id))
?? throw new EntityNotFoundException(typeof(ListFormImport), id);
// Izin logic process
if (!await _authManager.CanAccess(session.ListFormCode, AuthorizationTypeEnum.Import))
throw new Volo.Abp.UserFriendlyException(L[AppErrorCodes.NoAuth]);
await _importSessionRepository.DeleteAsync(id);
await _importSessionRepository.DeleteAsync(id, autoSave: true);
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Erp.Languages.Entities;
@ -10,7 +11,7 @@ using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using static Erp.Settings.SettingsConsts;
using Volo.Abp.Uow;
namespace Erp.Platform.ListForms;
@ -41,6 +42,7 @@ public class ListFormWizardAppService(
private readonly ILookupNormalizer lookupNormalizer = LookupNormalizer;
private readonly string cultureNameDefault = PlatformConsts.DefaultLanguage;
[UnitOfWork]
public async Task Create(WizardCreateInputDto input)
{
var listFormCode = input.ListFormCode;
@ -48,42 +50,45 @@ public class ListFormWizardAppService(
var titleLangKey = PlatformConsts.Wizard.WizardKeyTitle(listFormCode);
var code = PlatformConsts.Wizard.WizardKey(listFormCode);
//Dil
// Dil Key
// App.Platform.Form-001 => Text EN: ..., Text TR: ...
// App.Platform.Form-001.Title => Text EN: ..., Text TR: ...
//Dil - Language Keys
await CreateLangKey(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
await CreateLangKey(titleLangKey, input.LanguageTextTitleEn, input.LanguageTextTitleTr);
await CreateLangKey(PlatformConsts.Wizard.WizardKeyDesc(listFormCode), input.LanguageTextDescEn, input.LanguageTextDescTr);
//Permission
// Group Name => Platform
// App.Platform.Form-001 => LanguageKey: App.Platform.Form-001.Menu, Parent Name: Ø
// App.Platform.Form-001.Create => LanguageKey: App.Create, Parent Name: App.Platform.Form-001
// App.Platform.Form-001.Update => LanguageKey: App.Update, Parent Name: App.Platform.Form-001
// App.Platform.Form-001.Delete => LanguageKey: App.Delete, Parent Name: App.Platform.Form-001
// Dil
// Dil Key
//Permission Group
var groupName = input.PermissionGroupName ?? PlatformConsts.AppName;
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName));
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
await CreateLangKey(groupName, groupName, groupName);
}
var permRead = await repoPerm.FirstOrDefaultAsync(a => a.GroupName == groupName && a.Name == code) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both));
var permCreate = await repoPerm.FirstOrDefaultAsync(a => a.GroupName == groupName && a.Name == PlatformConsts.Wizard.PermCreate(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermCreate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyCreate, true, MultiTenancySides.Both));
var permUpdate = await repoPerm.FirstOrDefaultAsync(a => a.GroupName == groupName && a.Name == PlatformConsts.Wizard.PermUpdate(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermUpdate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyUpdate, true, MultiTenancySides.Both));
var permDelete = await repoPerm.FirstOrDefaultAsync(a => a.GroupName == groupName && a.Name == PlatformConsts.Wizard.PermDelete(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermDelete(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyDelete, true, MultiTenancySides.Both));
var permExport = await repoPerm.FirstOrDefaultAsync(a => a.GroupName == groupName && a.Name == PlatformConsts.Wizard.PermExport(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermExport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyExport, true, MultiTenancySides.Both));
// Permission'ları tek seferde kontrol et ve oluştur
var queryable = await repoPerm.GetQueryableAsync();
var existingPerms = await AsyncExecuter.ToListAsync(
queryable.Where(a => a.GroupName == groupName)
);
var permRead = existingPerms.FirstOrDefault(a => a.Name == code) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: false);
var permCreate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermCreate(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermCreate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermUpdate(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermUpdate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
var permDelete = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermDelete(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermDelete(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
var permExport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermExport(listFormCode)) ??
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermExport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
// Permission Grants - Bulk Insert
var adminUserName = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue;
var adminUser = await userRepository.FindByNormalizedUserNameAsync(lookupNormalizer.NormalizeName(adminUserName));
var adminRole = await roleRepository.FindByNormalizedNameAsync(lookupNormalizer.NormalizeName(PlatformConsts.AbpIdentity.User.AdminRoleName));
await permissionGrantRepository.InsertManyAsync(
[
new PermissionGrant(Guid.NewGuid(), permRead.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
@ -91,17 +96,11 @@ public class ListFormWizardAppService(
new PermissionGrant(Guid.NewGuid(), permUpdate.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
new PermissionGrant(Guid.NewGuid(), permDelete.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
new PermissionGrant(Guid.NewGuid(), permExport.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
]);
], autoSave: false);
//Menu
// App.Platform.Form-001 => LanguageKey: App.Platform.Form-001.Menu, Permission: App.Platform.Form-001, Parent Name: Platform
// Permission
// Group Name
// Dil
// Dil Key
// Dil
// Dil Key
var menuParent = await repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
//Menu Parent
var menuQueryable = await repoMenu.GetQueryableAsync();
var menuParent = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == input.MenuParentCode));
if (menuParent == null)
{
await CreateLangKey(PlatformConsts.Wizard.WizardKeyParent(listFormCode), input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
@ -110,9 +109,11 @@ public class ListFormWizardAppService(
Code = input.MenuParentCode,
DisplayName = PlatformConsts.Wizard.WizardKeyParent(listFormCode),
IsDisabled = false,
});
}, autoSave: false);
}
var menu = await repoMenu.FirstOrDefaultAsync(a => a.Code == code) ??
//Menu
var menu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code)) ??
await repoMenu.InsertAsync(new Menu
{
Code = code,
@ -125,10 +126,11 @@ public class ListFormWizardAppService(
CssClass = null,
Url = PlatformConsts.Wizard.MenuUrl(listFormCode),
RequiredPermissionName = permRead.Name
});
}, autoSave: false);
//Data Source
var dataSource = repoDataSource.FirstOrDefaultAsync(a => a.Code == input.DataSourceCode);
var dataSourceQueryable = await repoDataSource.GetQueryableAsync();
var dataSource = await AsyncExecuter.FirstOrDefaultAsync(dataSourceQueryable.Where(a => a.Code == input.DataSourceCode));
if (dataSource is null)
{
await repoDataSource.InsertAsync(new DataSource
@ -136,7 +138,7 @@ public class ListFormWizardAppService(
Code = input.DataSourceCode,
DataSourceType = input.DataSourceConnectionString.IndexOf("Server") >= 0 ? DataSourceTypeEnum.Mssql : DataSourceTypeEnum.Postgresql,
ConnectionString = input.DataSourceConnectionString
});
}, autoSave: false);
}
//ListForm
@ -148,7 +150,7 @@ public class ListFormWizardAppService(
Title = titleLangKey,
CultureName = PlatformConsts.DefaultLanguage,
Description = PlatformConsts.Wizard.WizardKeyDesc(listFormCode),
SelectCommandType = input.SelectCommandType, //SelectCommandTypeEnum.Table,
SelectCommandType = input.SelectCommandType,
SelectCommand = input.SelectCommand,
KeyFieldName = input.KeyFieldName,
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
@ -159,21 +161,28 @@ public class ListFormWizardAppService(
U = permUpdate.Name,
D = permDelete.Name
}),
});
}, autoSave: true);
}
private async Task<LanguageKey> CreateLangKey(string key, string textEn, string textTr)
{
var res = PlatformConsts.AppName;
var langKey = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == res && a.Key == key)
?? await repoLangKey.InsertAsync(new LanguageKey { ResourceName = res, Key = key });
var langTextEn = await repoLangText.FirstOrDefaultAsync(a => a.ResourceName == res && a.Key == langKey.Key && a.CultureName == cultureNameDefault)
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = cultureNameDefault, Value = textEn });
var langTextTitleTr = await repoLangText.FirstOrDefaultAsync(a => a.ResourceName == res && a.Key == langKey.Key && a.CultureName == LanguageCodes.Tr)
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = LanguageCodes.En, Value = textTr });
var keyQueryable = await repoLangKey.GetQueryableAsync();
var langKey = await AsyncExecuter.FirstOrDefaultAsync(keyQueryable.Where(a => a.ResourceName == res && a.Key == key))
?? await repoLangKey.InsertAsync(new LanguageKey { ResourceName = res, Key = key }, autoSave: false);
var textQueryable = await repoLangText.GetQueryableAsync();
var existingTexts = await AsyncExecuter.ToListAsync(
textQueryable.Where(a => a.ResourceName == res && a.Key == langKey.Key)
);
var langTextEn = existingTexts.FirstOrDefault(a => a.CultureName == cultureNameDefault)
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = cultureNameDefault, Value = textEn }, autoSave: false);
var langTextTr = existingTexts.FirstOrDefault(a => a.CultureName == LanguageCodes.Tr)
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = LanguageCodes.Tr, Value = textTr }, autoSave: false);
return langKey;
}
}