abpframework / abp

Open-source web application framework for ASP.NET Core! Offers an opinionated architecture to build enterprise software solutions with best practices on top of the .NET. Provides the fundamental infrastructure, cross-cutting-concern implementations, startup templates, application modules, UI themes, tooling and documentation.
https://abp.io
GNU Lesser General Public License v3.0
12.87k stars 3.44k forks source link

This unit of work already contains a transaction API for the given key #19722

Closed abdullahshaqaliah closed 5 months ago

abdullahshaqaliah commented 5 months ago

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.UnitOfWorkDbContextProvider1.CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.CreateDbContextAsync(IUnitOfWork unitOfWork) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.CreateDbContextAsync(IUnitOfWork unitOfWork, String connectionStringName, String connectionString) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.GetDbContextAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository2.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.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at EraTech.StorageService.AttachmentAppService1.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.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 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.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.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, Func3 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.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at EraTech.AspNetCore.Mvc.UI.UploaderManager.UploadFileAsync(IFormFile file, Nullable1 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

    public async Task<InitiativeVersion> GetForSaveEditAsync(Guid id, decimal version, CancellationToken cancellationToken = default)
    {
        var query = await GetQueryableAsync().ConfigureAwait(false);
        var initiative = await query.FirstOrDefaultAsync(e => e.Id == id && e.Version == version, cancellationToken);
        if (initiative == null)
        {
            throw new Volo.Abp.Domain.Entities.EntityNotFoundException(typeof(Initiative), id);
        }
        return initiative;
    }
maliming commented 5 months ago

hi

Try to make your GetForSaveEditAsync method virtual.

Can you also share the code of EraTech.StorageService.AttachmentAppService1.UploadAsync(UploadInputDto input)

abdullahshaqaliah commented 5 months ago

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

abdullahshaqaliah commented 5 months ago

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]));
            }

        }
    }

}
maliming commented 5 months ago

Great, Is your problem solved?

abdullahshaqaliah commented 5 months ago

Yes, thank you