递归替换LINQ表达式中的参数

时间:2018-12-05 23:13:49

标签: c# performance entity-framework linq lambda

我正在研究一个支持本地化实体的项目。 目前,我具有以下结构:

实体:

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都有一个仅选择数据库上所需列的投影。

在存储库中,我正在对翻译实体表执行查询,并使用Navigation属性返回所有者实体:

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

谢谢

0 个答案:

没有答案