Closed abdullahshaqaliah closed 5 months ago
hi
Try to make your GetForSaveEditAsync
method virtual
.
Can you also share the code of EraTech.StorageService.AttachmentAppService1.UploadAsync(UploadInputDto input)
Hi I found the problem with my code
public virtual async Task CommitUploadsAsync()
{
if (_files.Any())
{
await Task.WhenAll(_files).ConfigureAwait(false);
}
}
Because this a layer application I used to auto-upload any attachment files with the view model and save the file information in the database and this is my code before for attachments
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using EraTech.AspNetCore.Mvc.UI.Attributes;
using EraTech.AspNetCore.Mvc.UI.Theme.Shared.Models;
using EraTech.StorageService;
using EraTech.StorageService.FileManagers;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Users;
namespace EraTech.AspNetCore.Mvc.UI;
public class UploaderManager : IUploaderManager, ITransientDependency
{
private readonly IFileManagerAppService _fileManagerAppService ;
private readonly ICurrentUser _currentUser;
private readonly IGuidGenerator _guidGenerator;
private List<Task<FileDto>> _files;
public UploaderManager(IFileManagerAppService fileManagerAppService, ICurrentUser currentUser, IGuidGenerator guidGenerator)
{
_fileManagerAppService = fileManagerAppService;
_currentUser = currentUser;
_files = new List<Task<FileDto>>();
_guidGenerator = guidGenerator;
}
public async Task StartUploadsAsync(object model)
{
await AddFromStorageAttributeAsync(model).ConfigureAwait(false);
await AddFromImageListAsync(model).ConfigureAwait(false);
}
public virtual async Task CommitUploadsAsync()
{
if (_files.Any())
{
await Task.WhenAll(_files).ConfigureAwait(false);
}
}
public virtual async Task<FileDto> UploadFileAsync(IFormFile file, Guid? id = default)
{
if (file == null) { return new(); }
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
var attachment = await _fileManagerAppService.UploadAsync(
new UploadInputDto
{
AttachmentId = id,
Name = file.FileName,
UserId = _currentUser.GetId(),
ContentType = file.ContentType,
Content = memoryStream.ToArray()
}
);
return attachment;
}
}
public Task AddFileAsync(IFormFile file, Guid? id = null)
{
_files.Add(UploadFileAsync(file, id));
return Task.CompletedTask;
}
public async Task<List<Task<FileDto>>> GetFilesAsync()
{
return await Task.FromResult(_files);
}
protected virtual async Task AddFromStorageAttributeAsync(object model)
{
var props = model.GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(StorageAttribute))).ToList();
if (!props.IsNullOrEmpty())
{
foreach (var prop in props)
{
IFormFile file = prop.GetValue(model).As<IFormFile>();
if (file != null)
{
var storageAttr = prop.GetCustomAttribute<StorageAttribute>();
var storageId = _guidGenerator.Create();
var storageIdProp = model.GetType().GetProperty(storageAttr.OtherPropertyStorageId);
storageIdProp.SetValue(model, storageId);
if (!storageAttr.OtherPropertyStorageFileName.IsNullOrEmpty())
{
var fileNameProp = model.GetType().GetProperty(storageAttr.OtherPropertyStorageFileName);
fileNameProp.SetValue(model, file.FileName);
}
_files.Add(UploadFileAsync(file, storageId));
}
}
}
await Task.CompletedTask;
}
protected virtual async Task AddFromImageListAsync(object model)
{
var props = model.GetType().GetProperties().Where(prop => prop.PropertyType == typeof(List<ImageTranslationItem>)).ToList();
if (!props.IsNullOrEmpty())
{
foreach (var prop in props)
{
var files = prop.GetValue(model).As<List<ImageTranslationItem>>();
if (files.IsNullOrEmpty())
continue;
foreach (var file in files.Where(x => x.Content != null))
{
file.StorageId = _guidGenerator.Create();
_files.Add(UploadFileAsync(file.Content, file.StorageId));
}
files.RemoveAll(x => (x.Removed == 1 && x.Content == null) || !x.StorageId.HasValue);
}
}
await Task.CompletedTask;
}
}
and my view model code
public class UpdateViewModel
{
[HiddenInput]
public Guid Id { get; set; }
[HiddenInput]
public decimal Version { get; set; }
[HiddenInput]
public Guid? ImageId { get; set; }
[FileUploadExtensions]
[FileUploadSize()]
[Image(nameof(ImageId), ContainerHeight = 125, ContainerWidth = 125, Style = MetronicImageInputStyle.Outline)]
[Storage(nameof(ImageId))]
[Display(Name = "Logo")]
public IFormFile Image { get; set; }
[Required]
[StringLength(InitiativeConsts.MaxNameLength, MinimumLength = InitiativeConsts.MinNameLength)]
[Display(Name = "VersionName")]
[Css("form-control-solid")]
public string DisplayName { get; set; }
[StringLength(InitiativeConsts.MaxDescriptionLength, MinimumLength = InitiativeConsts.MinDescriptionLength)]
[TextArea]
[Css("form-control-solid h-100px")]
public string Description { get; set; }
[Display(Name ="Project")]
[Select2]
[SelectItems(nameof(Projects))]
[Required]
[Css("form-control-solid")]
[ContainerCSS("col-6")]
public Guid? ProjectId { get; set; }
[Label]
[ContainerCSS("col-6 mt-2")]
public string StrategicGoal { get; set; }
[Display(Name = "Category")]
[Select2]
[SelectItems(nameof(Categories))]
[Required]
[Css("form-control-solid")]
[ContainerCSS("col-6")]
public Guid? CategoryId { get; set; }
[Display(Name = "TargetGroup")]
[Select2]
[SelectItems(nameof(Groups))]
[Required]
[Css("form-control-solid")]
[ContainerCSS("col-6")]
public Guid? UserTypeId { get; set; }
[Display(Name ="Leader")]
[Required]
[Select2("/api/sm/data-lookup/users",dataTextField:"text", template: "Select2UserFormat")]
[SelectItems(nameof(Leader))]
[Css("form-control-solid")]
[ContainerCSS("col-6")]
public Guid? LeaderId { get; set; }
[Display(Name = "Priority")]
[Select2]
[SelectItems(nameof(Orders))]
[Required]
[Css("form-control-solid")]
[ContainerCSS("col-6")]
public int DisplayOrder { get; set; }
[StringLength(InitiativeConsts.MaxDescriptionLength, MinimumLength = InitiativeConsts.MinDescriptionLength)]
[TextArea]
[Css("form-control-solid h-100px")]
public string LessonsLearned { get; set; }
}
The storage attribute code
using System;
namespace EraTech.AspNetCore.Mvc.UI.Attributes;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class StorageAttribute : Attribute
{
public string OtherPropertyStorageId { get; set; }
public string OtherPropertyStorageFileName { get; set; }
public StorageAttribute(string otherPropertyStorageId)
{
OtherPropertyStorageId = otherPropertyStorageId;
}
public StorageAttribute(string otherPropertyStorageId, string otherPropertyStorageFileName)
{
OtherPropertyStorageId = otherPropertyStorageId;
OtherPropertyStorageFileName = otherPropertyStorageFileName;
}
}
There are attributes I make as custom to help me with low-code development
My code for auto development page model for create or update
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace EraTech.AspNetCore.Mvc.UI;
public abstract class EraTechCreateOrUpdateBasePageModel<TService, TOutput, TKey, TViewModel> : EraTechPageModel
where TService : IApplicationService
{
protected TService AppService => LazyServiceProvider.LazyGetRequiredService<TService>();
[BindProperty(SupportsGet = true)]
public TKey Id { get; set; }
[BindProperty()]
public TViewModel Info { get; set; }
public TOutput OutputDto { get; set; }
protected bool CheckkeyHasValue()
{
return !EqualityComparer<TKey>.Default.Equals(Id, default);
}
/// <summary>
/// Call after OnGet method executes
/// </summary>
/// <returns></returns>
protected virtual async Task OnGetExecutedAsync()
{
await Task.CompletedTask;
}
/// <summary>
/// Call before OnGet method executes
/// </summary>
/// <returns></returns>
protected virtual async Task OnGetExecutingAsync()
{
await Task.CompletedTask;
}
/// <summary>
/// Call after OnPost method executes
/// </summary>
/// <returns></returns>
protected virtual async Task OnPostExecutedAsync()
{
await Task.CompletedTask;
}
/// <summary>
/// Call before OnPost method executes
/// </summary>
/// <returns></returns>
protected virtual async Task OnPostExecutingAsync()
{
await Task.CompletedTask;
}
/// <summary>
/// Call Before OnPost method executes
/// </summary>
/// <returns></returns>
protected virtual async Task OnUploadExecutingAsync()
{
await UploaderManager.StartUploadsAsync(Info).ConfigureAwait(false);
}
/// <summary>
/// Call after OnPost method executes
/// </summary>
/// <returns></returns>
protected virtual async Task OnUploadExecutedAsync()
{
await UploaderManager.CommitUploadsAsync();
}
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace EraTech.AspNetCore.Mvc.UI;
public abstract class EraTechCreateOrUpdatePageModel<TService, TOutput, TKey, TViewModel> :
EraTechCreateOrUpdateBasePageModel<TService, TOutput, TKey, TViewModel>
where TService : IApplicationService
{
protected async Task<TOutput> CreateAsync<TCreateService, TCreateInput>() where TCreateService : ICreateAppService<TOutput, TCreateInput>
{
var service = LazyServiceProvider.GetRequiredService<TCreateService>();
await OnUploadExecutingAsync().ConfigureAwait(false);
var createDto = ObjectMapper.Map<TViewModel, TCreateInput>(Info);
OutputDto = await service.CreateAsync(createDto).ConfigureAwait(false);
await OnUploadExecutedAsync().ConfigureAwait(false);
return OutputDto;
}
protected async Task<TOutput> UpdateAsync<TUpdateService, TUpdateInput>() where TUpdateService : IUpdateAppService<TOutput, TKey, TUpdateInput>
{
var service = LazyServiceProvider.GetRequiredService<TUpdateService>();
await OnUploadExecutingAsync().ConfigureAwait(false);
var updateDto = ObjectMapper.Map<TViewModel, TUpdateInput>(Info);
OutputDto = await service.UpdateAsync(Id, updateDto).ConfigureAwait(false);
await OnUploadExecutedAsync().ConfigureAwait(false);
return OutputDto;
}
}
public abstract class EraTechCreateOrUpdatePageModel<TEntityDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput, TService, TViewModel> :
EraTechCreateOrUpdatePageModel<TService, TEntityDto, TKey, TViewModel>
where TService : ICrudAppService<TEntityDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TViewModel : class
{
public virtual async Task OnGetAsync()
{
await OnGetExecutingAsync().ConfigureAwait(false);
if (CheckkeyHasValue())
{
OutputDto = await AppService.GetAsync(Id).ConfigureAwait(false);
Info = ObjectMapper.Map<TEntityDto, TViewModel>(OutputDto);
}
await OnGetExecutedAsync().ConfigureAwait(false);
}
public virtual async Task<IActionResult> OnPostAsync()
{
await OnPostExecutingAsync().ConfigureAwait(false);
ValidateModel(Info);
if (CheckkeyHasValue())
{
OutputDto = await UpdateAsync<TService, TUpdateInput>().ConfigureAwait(false);
}
else
{
OutputDto = await CreateAsync<TService,TCreateInput>().ConfigureAwait(false);
}
await OnPostExecutedAsync().ConfigureAwait(false);
return new OkObjectResult(OutputDto);
}
}
public class EraTechCreateOrUpdatePageModel<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput, TService, TViewModel> :
EraTechCreateOrUpdatePageModel<TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput, TService, TViewModel>
where TService :
ICrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TViewModel : class
{
}
public class EraTechCreateOrUpdatePageModel<TEntityDto, TKey, TGetListInput, TCreateOrUpdateInput, TService, TViewModel> :
EraTechCreateOrUpdatePageModel<TEntityDto, TKey, TGetListInput, TCreateOrUpdateInput, TCreateOrUpdateInput, TService, TViewModel>
where TService : ICrudAppService<TEntityDto, TKey, TGetListInput, TCreateOrUpdateInput>
where TViewModel : class
{
}
public class EraTechCreateOrUpdatePageModel<TEntityDto, TKey, TGetListInput, TService, TViewModel> :
EraTechCreateOrUpdatePageModel<TEntityDto, TKey, TGetListInput, TEntityDto, TService, TViewModel>
where TService : ICrudAppService<TEntityDto, TKey, TGetListInput>
where TViewModel : class
{
}
Example
public class EditModalModel : EraTechCreateOrUpdatePageModel<SupportPhoneNumberDto, SupportPhoneNumberListDto, Guid, GetSupportPhoneNumberInput, SupportPhoneNumberCreateOrUpdateDto, SupportPhoneNumberCreateOrUpdateDto, ISupportPhoneNumberAppService, SupportPhoneNumberViewModel>
{
private IDataLookupRemoteService _dataLookupRemoteService => LazyServiceProvider.LazyGetRequiredService<IDataLookupRemoteService>();
public List<SelectListItem> CountriesList { get; set; }
protected override async Task OnGetExecutingAsync()
{
var countriesList = await _dataLookupRemoteService.GetCountiresListAsync();
CountriesList = countriesList.OrderBy(x => x.Name)
.Select(x => new SelectListItem(x.Name, x.Id.ToString()))
.ToList();
CountriesList.Insert(0,new SelectListItem("",""));
}
protected override Task OnPostExecutingAsync()
{
Info.CountryPhoneNumbers = new List<CountryPhoneNumberDto>();
Info.CountryPhoneNumbers.Add(new CountryPhoneNumberDto(Info.FirstPhoneNumber));
if (!Info.IsGlobal && !Info.SecondPhoneNumber.IsNullOrEmpty())
{
Info.CountryPhoneNumbers.Add(new CountryPhoneNumberDto(Info.SecondPhoneNumber));
}
return Task.CompletedTask;
}
[AutoMap(typeof(SupportPhoneNumberDto))]
[AutoMap(typeof(SupportPhoneNumberCreateOrUpdateDto), ReverseMap = true)]
public class SupportPhoneNumberViewModel : IValidatableObject
{
[HiddenInput]
public Guid Id { get; set; }
[Display(Name = "FormName:IsGlobal")]
public bool IsGlobal { get; set; }
[Display(Name = "FormName:Country")]
[Required]
[SelectItems(nameof(CountriesList))]
[Css("select2_country")]
public int? CountryId { get; set; }
[Display(Name = "FormName:FirstPhoneNumber")]
[Required]
[StringLength(maximumLength: SupportPhoneNumberConsts.MaxPhoneLength, MinimumLength = SupportPhoneNumberConsts.MinPhoneLength)]
public string FirstPhoneNumber { get; set; }
[StringLength(maximumLength: SupportPhoneNumberConsts.MaxPhoneLength, MinimumLength = SupportPhoneNumberConsts.MinPhoneLength)]
[Display(Name = "FormName:SecondPhoneNumber")]
[ContainerCSSIf(nameof(ShowSecondPhoneNumber),"second-number", "second-number d-done")]
public string SecondPhoneNumber { get; set; }
[HiddenInput]
public Guid? FlagId { get; set; }
[FileUploadExtensions]
[FileUploadSize()]
[Image(nameof(ImageId), ContainerHeight = 125, ContainerWidth = 125, Style = MetronicImageInputStyle.Outline)]
[Storage(nameof(FlagId))]
[Display(Name = "Logo")]
public IFormFile Flag{ get; set; }
[DynamicFormIgnore]
public List<CountryPhoneNumberDto> CountryPhoneNumbers { get; set; }
public bool ShowSecondPhoneNumber()
{
return !IsGlobal;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var l = validationContext.GetRequiredService<IStringLocalizer<CMSServiceResource>>();
if (FirstPhoneNumber.Equals(SecondPhoneNumber, StringComparison.OrdinalIgnoreCase))
{
yield return new ValidationResult(string.Format(l[CMSServiceDomainErrorCodes.SupporthoneNumbers.Validation.EqualFirstAndSecondPhoneNumber]));
}
}
}
}
Great, Is your problem solved?
Yes, thank you
Hi I using abp version 8.1.0 with no layer I keep getting this error
[22:44:27 ERR] This unit of work already contains a transaction API for the given key. Volo.Abp.AbpException: This unit of work already contains a transaction API for the given key. at Volo.Abp.Uow.UnitOfWork.AddTransactionApi(String key, ITransactionApi api) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider
1.CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.CreateDbContextAsync(IUnitOfWork unitOfWork) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider
1.CreateDbContextAsync(IUnitOfWork unitOfWork, String connectionStringName, String connectionString) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.GetDbContextAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository
2.InsertAsync(TEntity entity, Boolean autoSave, CancellationToken cancellationToken) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at EraTech.StorageService.AttachmentAppService
1.UploadAsync(UploadInputDto input) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue
1.ProceedAsync() at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func
3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue
1.ProceedAsync() at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope) at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func
3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at EraTech.AspNetCore.Mvc.UI.UploaderManager.UploadFileAsync(IFormFile file, Nullable
1 id) at EraTech.AspNetCore.Mvc.UI.UploaderManager.CommitUploadsAsync() at i3.SM.Pages.Initiatives.EditModel.OnPostAsync() in F:\vs\Web\i3.SM\i3.SM\Pages\Initiatives\Edit.cshtml.cs:line 73 at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Convert[T](Object taskAsObject) at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments) at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync() at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()`My code