I have a multilingual entity (the translated strings are stored in a separate table) and I would like to filter across the translated string of the current UI culture. If there is no translated string for the current UI culture, I would like to take and filter across the first default translated string of the entity.
I'm using .NET Core 2.1, EF Core 2.1, and the ABP Boilerplate 3.6.2 framework.
Here is my code. This function is filtering the DocumentCategory entity across the Name property of the DocumentCategoryTranslation related entity. I take the current UI culture translated string if any, else I take the first one. The returned object is simply a wrapper around my result.
public async Task<PagedResultDto<GetDocumentCategoryForView>> GetAll(GetAllDocumentCategoriesInput input)
{
var query = (from dc in _documentCategoryRepository.GetAllIncluding(dc => dc.Translations)
select new
{
dc,
t = dc.Translations.FirstOrDefault(t => t.Language == CultureInfo.CurrentUICulture.Name) ?? dc.Translations.First()
}).AsQueryable()
.WhereIf(!string.IsNullOrWhiteSpace(input.Filter), e => false || e.t.Name.ToLower().Contains(input.Filter.ToLower()));
var result = await query.ToListAsync();
return new PagedResultDto<GetDocumentCategoryForView>(
result.Count,
result.Select(o => new GetDocumentCategoryForView()
{
DocumentCategory = ObjectMapper.Map<DocumentCategoryDto>(o.dc)
}).ToList()
);
}
The exception I get :
System.InvalidOperationException: 'Query source (from DocumentCategoryTranslation t in value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Colnec.Documents.DocumentCategoryTranslation])) has already been associated with an expression.'
If I remove the GetAllIncluding(dc => dc.Translations) the query is working, but the mapping done at the end of the function throw a null exception (because it's using the Translations property of the dc entity).
The automapper extension function (from ABP framework). It sets the Name property of the destination object to the current UI culture translated string. The Translations property need to be loaded here.
public static void CreateMultiLingualMapFromEntity<TMultiLingualEntity, TMultiLingualEntityPrimaryKey, TTranslation, TDestination>(
this IMapperConfigurationExpression configuration, MultiLingualMapContext multiLingualMapContext)
where TTranslation : class, IEntityTranslation<TMultiLingualEntity, TMultiLingualEntityPrimaryKey>
where TMultiLingualEntity : IMultiLingualEntity<TTranslation>
{
configuration.CreateMap<TTranslation, TDestination>();
configuration.CreateMap<TMultiLingualEntity, TDestination>().BeforeMap((source, destination, context) =>
{
var translation = source.Translations.FirstOrDefault(pt => pt.Language == CultureInfo.CurrentUICulture.Name);
if (translation != null)
{
context.Mapper.Map(translation, destination);
return;
}
var defaultLanguage = multiLingualMapContext.SettingManager
.GetSettingValue(LocalizationSettingNames.DefaultLanguage);
translation = source.Translations.FirstOrDefault(pt => pt.Language == defaultLanguage);
if (translation != null)
{
context.Mapper.Map(translation, destination);
return;
}
translation = source.Translations.FirstOrDefault();
if (translation != null)
{
context.Mapper.Map(translation, destination);
}
});
}
I tried a lot of differents things, but I don't know how to handle this case properly. If some of you have a nice solution for this, I would really appreciate your help.
Have a great day! Alexandre