Я работаю над проектом по поддержке локализуемых объектов.В настоящее время у меня есть следующая структура:
Объекты:
public class Role : LocalizableEntity<RoleTranslation, Role, int>, IEntity<int>
{
[Required]
[MaxLength(150)]
public string Code { get; set; }
[LocalizableProperty]
[Required]
[MaxLength(150)]
public string Name { get; set; }
[LocalizableProperty]
[MaxLength(500)]
public string Description { get; set; }
public bool Active { get; set; }
public virtual ICollection<RolePermission> Permissions { get; set; }
public virtual ICollection<RoleTranslation> Translations { get; set; }
}
public class RoleTranslation : TranslationEntity<Role, int>, ITranslationEntity<Role, int>
{
[Required]
[MaxLength(150)]
public string Name { get; set; }
[MaxLength(500)]
public string Description { get; set; }
public virtual Permission OwnerEntity { get; set; }
public int OwnerEntityId { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
}
public class Permission : LocalizableEntity<PermissionTranslation, Permission, int>, IEntity<int>
{
[Required]
[MaxLength(150)]
public string Code { get; set; }
[LocalizableProperty]
[Required]
[MaxLength(250)]
public string Name { get; set; }
[LocalizableProperty]
[MaxLength(500)]
public string Description { get; set; }
public virtual ICollection<PermissionTranslation> Translations { get; set; }
}
public class PermissionTranslation : TranslationEntity<Permission, int>, ITranslationEntity<Permission, int>
{
[Required]
[MaxLength(250)]
public string Name { get; set; }
[MaxLength(500)]
public string Description { get; set; }
public virtual Role OwnerEntity { get; set; }
public int OwnerEntityId { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
}
public class RolePermission : Entity<int>
{
public int RoleId { get; set; }
public Role Role { get; set; }
public int PermissionId { get; set; }
public Permission Permission { get; set; }
}
Свойства, помеченные как [LocalizableProperty], не сопоставлены с базой данных.Только для целей только для чтения.
Dto:
public class RoleDTO : IDTO
{
public int Id { get; set; }
public string Description { get; set; }
public IEnumerable<PermissionBO> Permissions { get; set; }
public static Expression<Func<Role, RoleDTO>> Projection
{
get
{
return x => new RoleDTO()
{
Id = x.Id,
Description = x.Description,
Permissions = x.RolePermissions.AsQueryable().Select(y => y.Permission).Select(PermissionDTO.Projection).ToList()
};
}
}
}
public class PermissionDTO : IDTO
{
public int Id { get; set; }
public string Name { get; set; }
public static Expression<Func<Permission, PermissionDTO>> Projection
{
get
{
return x => new PermissionDTO()
{
Id = x.Id,
Name = x.Name
};
}
}
}
У каждого DTO есть проекция для выбора только необходимых столбцов в базе данных.
В хранилище I 'm выполнение запросов к таблице сущностей перевода и использование свойства навигации обратно к сущности владельца:
IQueryable<TTranslationEntity> queryableResult = _translationEntities
.Include(x => x.OwnerEntity)
.Where(x => x.Language.Name == options.LanguageName)
.AsQueryable();
// Projection
var projectionExpression = MyParameterReplacer.Convert(options.Projection);
IQueryable<TResult> projection = queryableResult.Select(projectionExpression);
Я пытаюсь перед тем, как выполнить проекцию, используя пользовательский ExpressionVisitor, перестроитьзаданную проекцию и заменить все параметры и элементы, чтобы соответствовать целевому IQueryable.Свойства, которые находятся в основной сущности выражения, должны быть x.OwnerEntity.PropertyName, а свойства, которые находятся в сущности перевода, выражением должны быть x.PropertyName
В настоящее время у меня есть это, но проблема в том, чтовложенные выражения неправильно посещаются.
private class MyExpressionVisitor<TOwnerEntity, TTranslationEntity> : ExpressionVisitor
{
private ReadOnlyCollection<ParameterExpression> _parameters;
protected override Expression VisitParameter(ParameterExpression node)
{
return (_parameters != null)
? _parameters.FirstOrDefault(p => p.Name == node.Name)
: (node.Type.GetInterfaces().Any(x => x == typeof(TOwnerEntity)) ? Expression.Parameter(typeof(TTranslationEntity), node.Name) : node);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
_parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda");
return Expression.Lambda(Visit(node.Body), _parameters);
}
protected override Expression VisitMember(MemberExpression node)
{
return base.VisitMember(node);
if (node.Member.DeclaringType == typeof(TOwnerEntity))
{
var targetParameter = _parameters.FirstOrDefault(x => x.Type == typeof(TTranslationEntity));
var ownerEntityProperty = typeof(TTranslationEntity).GetProperty("OwnerEntity"));
var memberOnOwnerEntity = ownerEntityProperty.PropertyType.GetProperty(node.Member.Name);
var memberOnTranslations = typeof(TTranslationEntity).GetProperty(node.Member.Name);
if (memberOnTranslations is null && ownerEntityProperty != null && memberOnOwnerEntity != null)
{
var ownerEntityPropertyExpression = Expression.Property(targetParameter, ownerEntityProperty);
return Expression.MakeMemberAccess(Visit(ownerEntityPropertyExpression), memberOnOwnerEntity);
}
else
{
return Expression.MakeMemberAccess(Visit(node.Expression), memberOnTranslations);
}
}
return base.VisitMember(node);
}
}
}
Спасибо