How to add new language to ABP template? - asp.net-core

I'm using free boilerplate (ASP.NET Core MVC & jQuery) from this site https://aspnetboilerplate.com/Templates
Is it possible to add new language support?
I already add localized .xml file, update 'abplanguages' table in database but it is not working. I'm changing language but text is still in english. The same situation with predefined languages already shipped with boilerplate like 'espanol-mexico' is not working but when I pick 'french' the page is translated.
This is weird because in documentation said it can be done.
https://aspnetboilerplate.com/Pages/Documents/Localization#extending-localization-sources
I wonder is it free template restriction?

inject IApplicationLanguageManager interface and use AddAsync() method to add a new language.
private readonly IApplicationLanguageManager _applicationLanguageManager;
public LanguageAppService(
IApplicationLanguageManager applicationLanguageManager,
IApplicationLanguageTextManager applicationLanguageTextManager,
IRepository<ApplicationLanguage> languageRepository)
{
_applicationLanguageManager = applicationLanguageManager;
_languageRepository = languageRepository;
_applicationLanguageTextManager = applicationLanguageTextManager;
}
protected virtual async Task CreateLanguageAsync(ApplicationLanguageEditDto input)
{
if (AbpSession.MultiTenancySide != MultiTenancySides.Host)
{
throw new UserFriendlyException(L("TenantsCannotCreateLanguage"));
}
var culture = CultureHelper.GetCultureInfoByChecking(input.Name);
await _applicationLanguageManager.AddAsync(
new ApplicationLanguage(
AbpSession.TenantId,
culture.Name,
culture.DisplayName,
input.Icon
)
{
IsDisabled = !input.IsEnabled
}
);
}
public static class CultureHelper
{
public static CultureInfo[] AllCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
public static bool IsRtl => CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
public static bool UsingLunarCalendar = CultureInfo.CurrentUICulture.DateTimeFormat.Calendar.AlgorithmType == CalendarAlgorithmType.LunarCalendar;
public static CultureInfo GetCultureInfoByChecking(string name)
{
try
{
return CultureInfo.GetCultureInfo(name);
}
catch (CultureNotFoundException)
{
return CultureInfo.CurrentCulture;
}
}
}
public class ApplicationLanguageEditDto
{
public virtual int? Id { get; set; }
[Required]
[StringLength(ApplicationLanguage.MaxNameLength)]
public virtual string Name { get; set; }
[StringLength(ApplicationLanguage.MaxIconLength)]
public virtual string Icon { get; set; }
/// <summary>
/// Mapped from Language.IsDisabled with using manual mapping in CustomDtoMapper.cs
/// </summary>
public bool IsEnabled { get; set; }
}

I figure it out. In my case it was incorrect build action property. In VS right click on localization source file: *.xml file -> Advanced -> Build action: Embedded resource.

Related

How to use ProtectedPersonalData attribute

I found the attribute class, ProtectedPersonalData (link), of ASP.NET Core Identity framework, but I can't seem to find any documentation on how to use it.
The documentation only says: Used to indicate that a something is considered personal data and should be protected.
At the end, I was able to encrypt the Identity User class fields (link) (e.g. email field), but not any property of an Identity User inheriting class.
public class ApplicationUser : IdentityUser {
[ProtectedPersonalData]
public string MyProperty { get; set; }
}
I added this to the Identity Config:
services.AddDefaultIdentity<ApplicationUser>(options => {
options.Stores.ProtectPersonalData = true;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Moreover, I implemented protector classes:
public class Lookup : ILookupProtector {
public string Protect(string keyId, string data) {
return new string(data?.Reverse().ToArray());
}
public string Unprotect(string keyId, string data) {
return new string(data?.Reverse().ToArray());
}
}
public class Protector : IPersonalDataProtector {
public string Protect(string data) {
return new string(data?.Reverse().ToArray());
}
public string Unprotect(string data) {
return new string(data?.Reverse().ToArray());
}
}
public class KeyRing : ILookupProtectorKeyRing {
public string this[string keyId] => "key";
public string CurrentKeyId => "key";
public IEnumerable<string> GetAllKeyIds() {
return new string[] { "key" };
}
}
It is possible to encrypt MyProperty field?
Please point me to information or provide some examples please.
Update:
I noticed that the code is never entering inside the Protect method for property MyProperty.
You need to add data annotation to the attribute qualifying as PersonalData, like this:
[ProtectedPersonalData]
[PersonalData]
public string Firstname { get; set; }
[ProtectedPersonalData]
[PersonalData]
public string Lastname { get; set; }
in order to activate the process you need register the services in your Startup.cs:
// ProtectedData
services.AddScoped<ILookupProtectorKeyRing, KeyRing>();
services.AddScoped<ILookupProtector, LookupProtector>();
services.AddScoped<IPersonalDataProtector, PersonalDataProtector>();
Example Repository
Here you can find an example repository with a project Blazor WASM with Microsoft Identity accounts and ProtectedData implementation.
https://github.com/nbiada/protecteddata-wasm-example

Akavache not storing/returning a NodaTime LocalDateTime

I need to store a NodaTime LocalDateTime in an Akavache cache.
I've created a simple app which takes the following class and stores/retrieves it in/from an Akavache cache:
public class TestModel
{
public string Name { get; set; }
public LocalDateTime StartDateTimeLocal {get; set;}
public DateTime StartDateTimeUtc {get;set;}
}
When this is stored in and retrieved from the cache, the StartDateTimeLocal property hasn't been populated.
It seems that Akavache isn't aware of how to serialise/deserialize a LocalDateTime.
Is it possible to register types with Akavache or supply a custom serialisation for unknown types?
Console application to demonstrate it:
using Akavache;
using NodaTime;
using System;
using System.Reactive.Linq;
namespace AkavacheNodaTimeCore
{
class Program
{
static TestModel BeforeModel;
static TestModel AfterModel;
static void Main(string[] args)
{
// Note that we're using Akavache 6.0.27, to match the version we're using in our live system.
BlobCache.ApplicationName = "AkavacheNodaTimeCore";
BlobCache.EnsureInitialized();
BeforeModel = new TestModel()
{
StartLocalDateTime = LocalDateTime.FromDateTime(DateTime.Now),
StartDateTime = DateTime.UtcNow,
};
Console.WriteLine($"Before:LocalDateTime='{BeforeModel.StartLocalDateTime}' DateTime='{BeforeModel.StartDateTime}'");
CycleTheModels();
Console.WriteLine($"After: LocalDateTime='{AfterModel.StartLocalDateTime}' DateTime='{AfterModel.StartDateTime}'");
Console.WriteLine("Note that Akavache retrieves DateTimes as DateTimeKind.Local, so DateTime before and after above will differ.");
Console.WriteLine("Press any key to continue.");
var y = Console.ReadKey();
}
/// <summary>
/// Puts a model into Akavache and retrieves a new one so we can compare.
/// </summary>
static async void CycleTheModels()
{
await BlobCache.InMemory.Invalidate("model");
await BlobCache.InMemory.InsertObject("model", BeforeModel);
AfterModel = await BlobCache.InMemory.GetObject<TestModel>("model");
}
}
}
TestModel class:
using NodaTime;
using System;
namespace AkavacheNodaTimeCore
{
public class TestModel
{
public string Name { get; set; }
public LocalDateTime StartLocalDateTime { get; set; }
public DateTime StartDateTime {get;set;}
}
}
I have added a Git repo with the above in a console application which demonstrates the problem.
You need to configure the JsonSerializerSettings that Akavache uses with Json.NET. You'll need a reference to NodaTime.Serialization.JsonNet, at which point you can create a serializer settings instance, configure it for Noda Time, then add that as a dependency in Splat (which Akavache uses). I haven't used Splat before, so it's possible that this isn't the right way of doing it, but it works with your example:
using Newtonsoft.Json;
using NodaTime.Serialization.JsonNet;
using Splat;
...
// This should be before any of your other code.
var settings = new JsonSerializerSettings();
settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
Locator.CurrentMutable.RegisterConstant(settings, typeof(JsonSerializerSettings));
It may be worth filing in issue in the Akavache repo to request more documentation for customization of serialization settings - the above works, but was guesswork and a little bit of source code investigation.

How to migrate a Complex Type to the .net Core Service Implementation

My first time using .net core.
I was able to build a functioning ,net core web application that calls data from my SQL server using Onion Layers. My layout is as below:
Architecture
Core
Application Services
Domain Services
Core.Entity
Infrastructure
UI
API
CemeteryAPI
Web
MVC Web Application
My HomeController has a PageModel with a Complex Type of Search, which consists of about 5 or so ints another 5-6 Lists. In the past I would have done:
var model = new Models.HomePageModel
{
Search = new Business.Search()
};
public partial class Search
{
public String Surname { get; set; }
public String Forename { get; set; }
public String Initials { get; set; }
//etc.
}
I have registered my Services on my startup in ConfigureServices and have attempted to inject this way
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ICemeteryRepository, CemeteryRepository>();
services.AddScoped<ICountryRepository, CountryRepository>();
//etc
services.AddScoped<ICemeteryService, CemeteryService>();
services.AddScoped<ICountryService, CountryService>();
}
CemeteryService
//ApplicationService
public class CemeteryService : AbstractUnitOfWorkService, ICemeteryService
{
public CemeteryService(IUnitOfWork uow) : base(uow) { }
public int Count()
{
return _unitOfWork.CemeteryRepository.Count();
}
public Cemetery Get(int id)
{
return _unitOfWork.CemeteryRepository.Get(id);
}
public List<Cemetery> List()
{
return _unitOfWork.CemeteryRepository.GetAll().ToList();
}
}
ICemeterRepository
//DomainService
public interface ICemeteryRepository : IRepository<Cemetery>
{
}
CemeteryRepository
public class CemeteryRepository : BaseRepository, ICemeteryRepository
{
public CemeteryRepository(SAWGPDBContext context) : base(context) { }
public int Count()
{
return _context.Cemetery.Count();
}
public Cemetery Get(int id)
{
return _context.Cemetery.Find(id);
}
public IEnumerable<Cemetery> GetAll()
{
return _context.Cemetery;
}
//etc
}
ICemeteryInterface
public interface ICemeteryService
{
int Count();
List<Cemetery> List();
//etc
}
public ActionResult Index([FromServices] ICasualtyService _CasualtyService, IPhotoService _PhotoService, ICountryService _CountryService, ICemeteryService _CemeteryService, IRegimentService _RegimentService)
var model = new Models.HomePageModel
{
Search = new SearchPageModel(_PhotoService, _CasualtyService, _CountryService, _CemeteryService, _RegimentService, )
};
This looked wrong but I couldn't find any proper examples as I wasn't sure what to look for exactly. The above returns
Model bound complex types must not be abstract or value types and must
have a parameterless constructor.
I presume I need to build a SearchService but I'm not entirely clear how to build one for a complex type. Any pointers would be appreciated.

How to display disk space not enough message in burn bootstrapper applications?

I wrote my own installer UI with burn bootstrapper. It has a WPF frontend. I have an EXE with 3 MSI pacakges included. So when I try to install it in a disk with not enough space, how can I show an error message dialog in my installer UI? Is there a callback using which I can find if there is enough disk space? Please advice.
I'm looking at doing the same.
The secret is to read and parse the BootstrapperApplicationData.xml.
You can then use InstalledSize attribute from the WixPackageProperties element. This link Getting Display Name from PackageID shows you how do read that file at runtime. Note that you'll have to add in the InstalledSize to the relevant structure.
It'll be up to you to go and check the disk space compared to the sum of those numbers and flag that up to the user prior to install.
This is a copy/paste of the some of my code:
using System.Collections.ObjectModel;
using System.Xml.Serialization;
public class PackageInfo
{
[XmlAttribute("Package")]
public string Id { get; set; }
[XmlAttribute("DisplayName")]
public string DisplayName { get; set; }
[XmlAttribute("Description")]
public string Description { get; set; }
[XmlAttribute("InstalledSize")]
public int InstalledSize { get; set; }
}
[XmlRoot("BootstrapperApplicationData", IsNullable = false, Namespace = "http://schemas.microsoft.com/wix/2010/BootstrapperApplicationData")]
public class BundleInfo
{
[XmlElement("WixPackageProperties")]
public Collection<PackageInfo> Packages { get; set; } = new Collection<PackageInfo>();
}
public static class BundleInfoLoader
{
private static readonly string bootstrapperApplicationData = "BootstrapperApplicationData.xml";
public static BundleInfo Load()
{
var bundleFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var path = Path.Combine(bundleFolder, bootstrapperApplicationData);
var xmlSerializer = new XmlSerializer(typeof(BundleInfo));
BundleInfo result;
using (var fileStream = new FileStream(path, FileMode.Open))
{
var xmlReader = XmlReader.Create(fileStream);
result = (BundleInfo)xmlSerializer.Deserialize(xmlReader);
}
return result;
}
}

Return Entity Framework objects over WCF

We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method