Browse Source

Merge pull request #5300 from abpframework/Carry-data-to-remote-exceptions

Fix: localization data is lost when exception is on remote
pull/5323/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
74367cafd4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
  2. 8
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpRemoteCallException.cs
  3. 6
      framework/src/Volo.Abp.Http/Volo/Abp/Http/RemoteServiceErrorInfo.cs
  4. 4
      framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj
  5. 22
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs
  6. 2
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs
  7. 10
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs
  8. 11
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs
  9. 10
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Localization/HttpClientTestResource.cs
  10. 6
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Localization/en.json
  11. 56
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
  12. 56
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json
  13. 41
      modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs
  14. 18
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs

@ -72,7 +72,7 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
}
var errorInfo = new RemoteServiceErrorInfo();
if (exception is IUserFriendlyException)
{
errorInfo.Message = exception.Message;
@ -101,6 +101,8 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
errorInfo.Message = L["InternalServerErrorMessage"];
}
errorInfo.Data = exception.Data;
return errorInfo;
}
@ -190,7 +192,6 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
return exception;
}
protected virtual RemoteServiceErrorInfo CreateDetailedErrorInfoFromException(Exception exception)
{
var detailBuilder = new StringBuilder();
@ -213,7 +214,7 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
detailBuilder.AppendLine(exception.GetType().Name + ": " + exception.Message);
//Additional info for UserFriendlyException
if (exception is IUserFriendlyException &&
if (exception is IUserFriendlyException &&
exception is IHasErrorDetails)
{
var details = ((IHasErrorDetails) exception).Details;

8
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpRemoteCallException.cs

@ -28,6 +28,14 @@ namespace Volo.Abp.Http.Client
: base(error.Message)
{
Error = error;
if (error.Data != null)
{
foreach (var dataKey in error.Data.Keys)
{
Data[dataKey] = error.Data[dataKey];
}
}
}
}
}

6
framework/src/Volo.Abp.Http/Volo/Abp/Http/RemoteServiceErrorInfo.cs

@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Volo.Abp.Http
{
@ -23,6 +25,8 @@ namespace Volo.Abp.Http
/// </summary>
public string Details { get; set; }
public IDictionary Data { get; set; }
/// <summary>
/// Validation errors if exists.
/// </summary>
@ -49,4 +53,4 @@ namespace Volo.Abp.Http
Code = code;
}
}
}
}

4
framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj

@ -14,4 +14,8 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\Http\Localization\en.json" />
</ItemGroup>
</Project>

22
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs

@ -2,8 +2,12 @@
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.DynamicProxying;
using Volo.Abp.Http.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.TestApp;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Http
{
@ -22,6 +26,24 @@ namespace Volo.Abp.Http
{
options.RemoteServices.Default = new RemoteServiceConfiguration("/");
});
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpHttpClientTestModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<HttpClientTestResource>("en")
.AddVirtualJson("/Volo/Abp/Http/Localization");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Volo.Abp.Http.DynamicProxying", typeof(HttpClientTestResource));
});
}
}
}

2
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs

@ -9,6 +9,8 @@ namespace Volo.Abp.Http.DynamicProxying
Task GetException1Async();
Task GetException2Async();
Task<DateTime> GetWithDateTimeParameterAsync(DateTime dateTime1);
Task<string> PostValueWithHeaderAndQueryStringAsync(string headerValue, string qsValue);

10
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs

@ -27,6 +27,14 @@ namespace Volo.Abp.Http.DynamicProxying
throw new UserFriendlyException("This is an error message!");
}
[HttpGet]
[Route("get-exception2")]
public Task GetException2Async()
{
throw new BusinessException("Volo.Abp.Http.DynamicProxying:10001")
.WithData("0","TEST");
}
[HttpGet]
[Route("get-with-datetime-parameter")]
public Task<DateTime> GetWithDateTimeParameterAsync(DateTime dateTime1)
@ -133,4 +141,4 @@ namespace Volo.Abp.Http.DynamicProxying
[FromQuery]
public DateTime FirstReleaseDate { get; set; }
}
}
}

11
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs

@ -1,8 +1,10 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Shouldly;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Localization;
using Volo.Abp.Localization;
using Xunit;
@ -32,6 +34,13 @@ namespace Volo.Abp.Http.DynamicProxying
exception.Error.Message.ShouldBe("This is an error message!");
}
[Fact]
public async Task GetException2Async()
{
var exception = await Assert.ThrowsAsync<AbpRemoteCallException>(async () => await _controller.GetException2Async());
exception.Error.Message.ShouldBe("Business exception with data: TEST");
}
[Fact]
public async Task GetWithDateTimeParameterAsync()
{
@ -151,4 +160,4 @@ namespace Volo.Abp.Http.DynamicProxying
}
}
}
}

10
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Localization/HttpClientTestResource.cs

@ -0,0 +1,10 @@
using Volo.Abp.Localization;
namespace Volo.Abp.Http.Localization
{
[LocalizationResourceName("HttpClientTest")]
public class HttpClientTestResource
{
}
}

6
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Localization/en.json

@ -0,0 +1,6 @@
{
"culture": "en",
"texts": {
"Volo.Abp.Http.DynamicProxying:10001": "Business exception with data: {0}"
}
}

56
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json

@ -35,34 +35,34 @@
"DisplayName:NewPasswordConfirm": "Confirm new password",
"PasswordChangedMessage": "Your password has been changed successfully.",
"PersonalSettingsSavedMessage": "Your personal settings has been saved successfully.",
"Identity.DefaultError": "An unknown failure has occurred.",
"Identity.ConcurrencyFailure": "Optimistic concurrency failure, object has been modified.",
"Identity.DuplicateEmail": "Email '{0}' is already taken.",
"Identity.DuplicateRoleName": "Role name '{0}' is already taken.",
"Identity.DuplicateUserName": "User name '{0}' is already taken.",
"Identity.InvalidEmail": "Email '{0}' is invalid.",
"Identity.InvalidPasswordHasherCompatibilityMode": "The provided PasswordHasherCompatibilityMode is invalid.",
"Identity.InvalidPasswordHasherIterationCount": "The iteration count must be a positive integer.",
"Identity.InvalidRoleName": "Role name '{0}' is invalid.",
"Identity.InvalidToken": "Invalid token.",
"Identity.InvalidUserName": "User name '{0}' is invalid, can only contain letters or digits.",
"Identity.LoginAlreadyAssociated": "A user with this login already exists.",
"Identity.PasswordMismatch": "Incorrect password.",
"Identity.PasswordRequiresDigit": "Passwords must have at least one digit ('0'-'9').",
"Identity.PasswordRequiresLower": "Passwords must have at least one lowercase ('a'-'z').",
"Identity.PasswordRequiresNonAlphanumeric": "Passwords must have at least one non alphanumeric character.",
"Identity.PasswordRequiresUpper": "Passwords must have at least one uppercase ('A'-'Z').",
"Identity.PasswordTooShort": "Passwords must be at least {0} characters.",
"Identity.RoleNotFound": "Role {0} does not exist.",
"Identity.UserAlreadyHasPassword": "User already has a password set.",
"Identity.UserAlreadyInRole": "User already in role '{0}'.",
"Identity.UserLockedOut": "User is locked out.",
"Identity.UserLockoutNotEnabled": "Lockout is not enabled for this user.",
"Identity.UserNameNotFound": "User {0} does not exist.",
"Identity.UserNotInRole": "User is not in role '{0}'.",
"Identity.PasswordConfirmationFailed": "Password does not match the confirm password.",
"Identity.StaticRoleRenamingErrorMessage": "Static roles can not be renamed.",
"Identity.StaticRoleDeletionErrorMessage": "Static roles can not be deleted.",
"Volo.Abp.Identity:DefaultError": "An unknown failure has occurred.",
"Volo.Abp.Identity:ConcurrencyFailure": "Optimistic concurrency failure, object has been modified.",
"Volo.Abp.Identity:DuplicateEmail": "Email '{0}' is already taken.",
"Volo.Abp.Identity:DuplicateRoleName": "Role name '{0}' is already taken.",
"Volo.Abp.Identity:DuplicateUserName": "User name '{0}' is already taken.",
"Volo.Abp.Identity:InvalidEmail": "Email '{0}' is invalid.",
"Volo.Abp.Identity:InvalidPasswordHasherCompatibilityMode": "The provided PasswordHasherCompatibilityMode is invalid.",
"Volo.Abp.Identity:InvalidPasswordHasherIterationCount": "The iteration count must be a positive integer.",
"Volo.Abp.Identity:InvalidRoleName": "Role name '{0}' is invalid.",
"Volo.Abp.Identity:InvalidToken": "Invalid token.",
"Volo.Abp.Identity:InvalidUserName": "User name '{0}' is invalid, can only contain letters or digits.",
"Volo.Abp.Identity:LoginAlreadyAssociated": "A user with this login already exists.",
"Volo.Abp.Identity:PasswordMismatch": "Incorrect password.",
"Volo.Abp.Identity:PasswordRequiresDigit": "Passwords must have at least one digit ('0'-'9').",
"Volo.Abp.Identity:PasswordRequiresLower": "Passwords must have at least one lowercase ('a'-'z').",
"Volo.Abp.Identity:PasswordRequiresNonAlphanumeric": "Passwords must have at least one non alphanumeric character.",
"Volo.Abp.Identity:PasswordRequiresUpper": "Passwords must have at least one uppercase ('A'-'Z').",
"Volo.Abp.Identity:PasswordTooShort": "Passwords must be at least {0} characters.",
"Volo.Abp.Identity:RoleNotFound": "Role {0} does not exist.",
"Volo.Abp.Identity:UserAlreadyHasPassword": "User already has a password set.",
"Volo.Abp.Identity:UserAlreadyInRole": "User already in role '{0}'.",
"Volo.Abp.Identity:UserLockedOut": "User is locked out.",
"Volo.Abp.Identity:UserLockoutNotEnabled": "Lockout is not enabled for this user.",
"Volo.Abp.Identity:UserNameNotFound": "User {0} does not exist.",
"Volo.Abp.Identity:UserNotInRole": "User is not in role '{0}'.",
"Volo.Abp.Identity:PasswordConfirmationFailed": "Password does not match the confirm password.",
"Volo.Abp.Identity:StaticRoleRenamingErrorMessage": "Static roles can not be renamed.",
"Volo.Abp.Identity:StaticRoleDeletionErrorMessage": "Static roles can not be deleted.",
"Identity.OrganizationUnit.DuplicateDisplayNameWarning": "There is already an organization unit with name {0}. Two units with same name can not be created in same level.",
"Identity.OrganizationUnit.MaxUserMembershipCount": "Maximum allowed organization unit membership count for a user",
"Volo.Abp.Identity:010001": "You can not delete your own account!",

56
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json

@ -35,34 +35,34 @@
"DisplayName:NewPasswordConfirm": "Yeni şifre (tekrar)",
"PasswordChangedMessage": "Şifreniz başarıyla değiştirildi.",
"PersonalSettingsSavedMessage": "Kişisel bilgileriniz başarıyla kaydedildi.",
"Identity.StaticRoleRenamingErrorMessage": "Bir Sabit rolün ismi değiştirilemez.",
"Identity.StaticRoleDeletionErrorMessage": "Bir Sabit rol silinemez.",
"Identity.DefaultError": "Bilinmeyen bir hata oluştu.",
"Identity.ConcurrencyFailure": "İyimser eşzamanlılık hatası. Nesne değiştirilmiş.",
"Identity.DuplicateEmail": "'{0}' email adresi zaten alınmış.",
"Identity.DuplicateRoleName": "'{0}' rol ismi zaten alınmış.",
"Identity.DuplicateUserName": "'{0}' kullanıcı adı zaten alınmış.",
"Identity.InvalidEmail": "'{0}' email adresi hatalı.",
"Identity.InvalidPasswordHasherCompatibilityMode": "Belirtilen PasswordHasherCompatibilityMode geçersiz.",
"Identity.InvalidPasswordHasherIterationCount": "Iterasyon sayısı sıfırdan büyük bir sayı olmalı.",
"Identity.InvalidRoleName": "'{0}' rol ismi geçersizdir.",
"Identity.InvalidToken": "Geçersiz token.",
"Identity.InvalidUserName": "'{0}' kullanıcı adı geçersiz, sadece harf ve rakamlardan oluşmalı.",
"Identity.LoginAlreadyAssociated": "Bu giriş bilgilerine sahip bir kullanıcı zaten var.",
"Identity.PasswordMismatch": "Hatalı şifre.",
"Identity.PasswordRequiresDigit": "Şifre en az bir sayı içermeli ('0'-'9').",
"Identity.PasswordRequiresLower": "Şifre en az bir küçük harf içermeli ('a'-'z').",
"Identity.PasswordRequiresNonAlphanumeric": "Şifre en az bir sayı ya da harf olmayan karakter içermeli.",
"Identity.PasswordRequiresUpper": "Şifre en az bir büyük harf içermeli ('A'-'Z').",
"Identity.PasswordTooShort": "Şifre en az {0} karakter uzunluğunda olmalı.",
"Identity.RoleNotFound": "{0} rolü bulunamadı.",
"Identity.UserAlreadyHasPassword": "Kullanıcının zaten bir şifresi var.",
"Identity.UserAlreadyInRole": "Kullanıcı zaten '{0}' rolünde.",
"Identity.UserLockedOut": "Kullanıcı hesabı kilitlenmiş.",
"Identity.UserLockoutNotEnabled": "Bu kullanıcı için hesap kilitleme etkin değil.",
"Identity.UserNameNotFound": "{0} kullanıcısı bulunamadı.",
"Identity.UserNotInRole": "Kullanıcı '{0}' rolünde değil.",
"Identity.PasswordConfirmationFailed": "Yeni şifre ile onay şifresi uyuşmuyor.",
"Volo.Abp.Identity:StaticRoleRenamingErrorMessage": "Bir Sabit rolün ismi değiştirilemez.",
"Volo.Abp.Identity:StaticRoleDeletionErrorMessage": "Bir Sabit rol silinemez.",
"Volo.Abp.Identity:DefaultError": "Bilinmeyen bir hata oluştu.",
"Volo.Abp.Identity:ConcurrencyFailure": "İyimser eşzamanlılık hatası. Nesne değiştirilmiş.",
"Volo.Abp.Identity:DuplicateEmail": "'{0}' email adresi zaten alınmış.",
"Volo.Abp.Identity:DuplicateRoleName": "'{0}' rol ismi zaten alınmış.",
"Volo.Abp.Identity:DuplicateUserName": "'{0}' kullanıcı adı zaten alınmış.",
"Volo.Abp.Identity:InvalidEmail": "'{0}' email adresi hatalı.",
"Volo.Abp.Identity:InvalidPasswordHasherCompatibilityMode": "Belirtilen PasswordHasherCompatibilityMode geçersiz.",
"Volo.Abp.Identity:InvalidPasswordHasherIterationCount": "Iterasyon sayısı sıfırdan büyük bir sayı olmalı.",
"Volo.Abp.Identity:InvalidRoleName": "'{0}' rol ismi geçersizdir.",
"Volo.Abp.Identity:InvalidToken": "Geçersiz token.",
"Volo.Abp.Identity:InvalidUserName": "'{0}' kullanıcı adı geçersiz, sadece harf ve rakamlardan oluşmalı.",
"Volo.Abp.Identity:LoginAlreadyAssociated": "Bu giriş bilgilerine sahip bir kullanıcı zaten var.",
"Volo.Abp.Identity:PasswordMismatch": "Hatalı şifre.",
"Volo.Abp.Identity:PasswordRequiresDigit": "Şifre en az bir sayı içermeli ('0'-'9').",
"Volo.Abp.Identity:PasswordRequiresLower": "Şifre en az bir küçük harf içermeli ('a'-'z').",
"Volo.Abp.Identity:PasswordRequiresNonAlphanumeric": "Şifre en az bir sayı ya da harf olmayan karakter içermeli.",
"Volo.Abp.Identity:PasswordRequiresUpper": "Şifre en az bir büyük harf içermeli ('A'-'Z').",
"Volo.Abp.Identity:PasswordTooShort": "Şifre en az {0} karakter uzunluğunda olmalı.",
"Volo.Abp.Identity:RoleNotFound": "{0} rolü bulunamadı.",
"Volo.Abp.Identity:UserAlreadyHasPassword": "Kullanıcının zaten bir şifresi var.",
"Volo.Abp.Identity:UserAlreadyInRole": "Kullanıcı zaten '{0}' rolünde.",
"Volo.Abp.Identity:UserLockedOut": "Kullanıcı hesabı kilitlenmiş.",
"Volo.Abp.Identity:UserLockoutNotEnabled": "Bu kullanıcı için hesap kilitleme etkin değil.",
"Volo.Abp.Identity:UserNameNotFound": "{0} kullanıcısı bulunamadı.",
"Volo.Abp.Identity:UserNotInRole": "Kullanıcı '{0}' rolünde değil.",
"Volo.Abp.Identity:PasswordConfirmationFailed": "Yeni şifre ile onay şifresi uyuşmuyor.",
"Identity.OrganizationUnit.DuplicateDisplayNameWarning": "{0} isminde bir birim zaten var. Aynı seviyede aynı isimli iki birim olamaz.",
"Identity.OrganizationUnit.MaxUserMembershipCount": "Bir kullanıcı için izin verilen en fazla organizasyon birimi sayısı",
"Volo.Abp.Identity:010001": "Kendi hesabınızı silemezsiniz!",

41
modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
@ -26,6 +27,41 @@ namespace Microsoft.AspNetCore.Identity
throw new AbpIdentityResultException(identityResult);
}
public static string[] GetValuesFromErrorMessage(this IdentityResult identityResult, IStringLocalizer localizer)
{
if (identityResult.Succeeded)
{
throw new ArgumentException(
"identityResult.Succeeded should be false in order to get values from error.");
}
if (identityResult.Errors == null)
{
throw new ArgumentException("identityResult.Errors should not be null.");
}
var error = identityResult.Errors.First();
var key = $"Volo.Abp.Identity:{error.Code}";
using (CultureHelper.Use(CultureInfo.GetCultureInfo("en")))
{
var englishLocalizedString = localizer[key];
if (englishLocalizedString.ResourceNotFound)
{
return Array.Empty<string>();
}
if (FormattedStringValueExtracter.IsMatch(error.Description, englishLocalizedString.Value,
out var values))
{
return values;
}
return Array.Empty<string>();
}
}
public static string LocalizeErrors(this IdentityResult identityResult, IStringLocalizer localizer)
{
if (identityResult.Succeeded)
@ -43,7 +79,7 @@ namespace Microsoft.AspNetCore.Identity
public static string LocalizeErrorMessage(this IdentityError error, IStringLocalizer localizer)
{
var key = $"Identity.{error.Code}";
var key = $"Volo.Abp.Identity:{error.Code}";
var localizedString = localizer[key];
@ -54,7 +90,8 @@ namespace Microsoft.AspNetCore.Identity
var englishLocalizedString = localizer[key];
if (!englishLocalizedString.ResourceNotFound)
{
if (FormattedStringValueExtracter.IsMatch(error.Description, englishLocalizedString.Value, out var values))
if (FormattedStringValueExtracter.IsMatch(error.Description, englishLocalizedString.Value,
out var values))
{
return string.Format(localizedString.Value, values.Cast<object>().ToArray());
}

18
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityResultException.cs

@ -18,7 +18,7 @@ namespace Volo.Abp.Identity
public AbpIdentityResultException([NotNull] IdentityResult identityResult)
: base(
code: $"Identity.{identityResult.Errors.First().Code}",
code: $"Volo.Abp.Identity:{identityResult.Errors.First().Code}",
message: identityResult.Errors.Select(err => err.Description).JoinAsString(", "))
{
IdentityResult = Check.NotNull(identityResult, nameof(identityResult));
@ -32,7 +32,21 @@ namespace Volo.Abp.Identity
public virtual string LocalizeMessage(LocalizationContext context)
{
return IdentityResult.LocalizeErrors(context.LocalizerFactory.Create<IdentityResource>());
var localizer = context.LocalizerFactory.Create<IdentityResource>();
SetData(localizer);
return IdentityResult.LocalizeErrors(localizer);
}
protected virtual void SetData(IStringLocalizer localizer)
{
var values = IdentityResult.GetValuesFromErrorMessage(localizer);
for (var index = 0; index < values.Length; index++)
{
Data[index.ToString()] = values[index];
}
}
}
}

Loading…
Cancel
Save