生成带有嵌套实体的实体的动态选择表达式

3
我有一个类,其中有一个类型为另一个实体的属性。就像下面这样:
public class InspectionTopicDataAllViewModel 
{
    public Guid Id { get; set; }
    public Guid? UploadedFileId { get; set; }
    public int InspectionId { get; set; }
    public int InspectionTopicId { get; set; }

    public SparseDataViewModel SparseData { get; set; }
}

public class SparseDataViewModel : SparseData
{        
}

我需要编写一个动态选择表达式,只选择我想要的列。
我将其编写如下:
private static Func<InspectionTopicDataAll, InspectionTopicDataAllViewModel> GetSparseInitExpression(List<string> columns)
    {
        Expression<Func<InspectionTopicDataAll, InspectionTopicDataAllViewModel>> commonSelector =
           x => new InspectionTopicDataAllViewModel()
           {
               Id = x.Id,
               InspectionId = x.InspectionId,
               InspectionTopicId = x.InspectionTopicId,
               UploadedFileId = x.UploadedFileId,
           };


        // input parameter "x"
        var xParameter = Expression.Parameter(typeof(InspectionTopicDataAll), "x");           

        var xNew = Expression.New(typeof(InspectionTopicDataAllViewModel));

        var sNew = Expression.New(typeof(SparseDataViewModel));

        var bindings = new List<MemberBinding>();

        // create initializers
        foreach (var column in columns)
        {               
            Expression srcBody = xParameter;

            foreach (var member in column.Split('.'))
            {
                srcBody = Expression.PropertyOrField(srcBody, member);                
            }

            var destMember = srcBody as MemberExpression;
            // property "Field1"
            var propInfo = destMember?.Member as PropertyInfo;

            if (propInfo != null)
            {

                bindings.Add(Expression.Bind(propInfo, srcBody));
            }                               
        }

        var sInit = Expression.MemberInit(sNew, bindings);                                  

        var zeroth = ((MemberInitExpression)commonSelector.Body);
        var param = commonSelector.Parameters[0];
        List<MemberBinding> newBindings = new List<MemberBinding>(zeroth.Bindings.OfType<MemberAssignment>());

        var spNestedType = typeof (InspectionTopicDataAllViewModel).GetProperty(nameof(SparseData));

        newBindings.Add(Expression.Bind(spNestedType, sInit));

        var newInit = Expression.MemberInit(xNew, newBindings);

        var childSelector = Expression.Lambda<Func<InspectionTopicDataAll, InspectionTopicDataAllViewModel>>(newInit, xParameter);

        return childSelector.Compile();
}

在我的主查询中,使用以下函数:

 var model = worksheetService.GetInspectionTopicData(inspectionTopicId, inspectionId, discrimintor, value).Select(GetSparseInitExpression(columns)).AsQueryable();

使用此函数,我可以生成以下所需的表达式:
x => new InspectionTopicDataAllViewModel() 
{
    Id = x.Id, 
    InspectionId = x.InspectionId,
    InspectionTopicId = x.InspectionTopicId, 
    UploadedFileId = x.UploadedFileId, 
    SparseData = new SparseDataViewModel() {
        MusteriNo = x.SparseData.MusteriNo, 
        MusteriAdi = x.SparseData.MusteriAdi, 
        CekNo = x.SparseData.CekNo, 
        Banka = x.SparseData.Banka, 
        CekTarihi = x.SparseData.CekTarihi, 
        Meblag = x.SparseData.Meblag, 
        PCN = x.SparseData.PCN, 
        Kesideci = x.SparseData.Kesideci}}}

但是当我运行表达式时,会抛出以下错误:

变量“x”的类型为“InternalControl.Domain.Entities.InspectionTopicDataAll”,被引用于范围“”,但未定义

我不知道该如何解决这个错误... 更新: 感谢@MBoros的帮助,对于后来可能需要这种类型代码的人,尽管代码可以工作并且可以从数据库检索结果,但选择语句并没有按预期工作,并且所有列都从数据库中提取,因此为了解决这个问题,我使用了下面的代码:希望能帮助其他试图做这种事情的人。
 private static Expression<Func<InspectionTopicDataAll, InspectionTopicDataAllViewModel>> GetCustomSelectExpression(List<string> columns)
    {
        Expression<Func<InspectionTopicDataAll, InspectionTopicDataAllViewModel>> commonSelector =
           x => new InspectionTopicDataAllViewModel()
           {
               Id = x.Id,
               InspectionId = x.InspectionId,
               InspectionTopicId = x.InspectionTopicId,
               UploadedFileId = x.UploadedFileId,
           };


        // input parameter "x"                    
        var xParameter = commonSelector.Parameters[0];

        var xNew = Expression.New(typeof(InspectionTopicDataAllViewModel));

        var sNew = Expression.New(typeof(SparseDataViewModel));

        var bindings = new List<MemberBinding>();

        // create initializers
        foreach (var column in columns)
        {               
            Expression srcBody = xParameter;

            foreach (var member in column.Split('.'))
            {
                srcBody = Expression.PropertyOrField(srcBody, member);                
            }

            var destColName = column.Substring(column.LastIndexOf('.')+1);               
            // property "Field1"
            var propInfo = typeof(SparseDataViewModel).GetProperty(destColName);


            if (propInfo != null)
            {
                bindings.Add(Expression.Bind(propInfo, srcBody));
            }                               
        }

        var sInit = Expression.MemberInit(sNew, bindings);                                  

        var zeroth = ((MemberInitExpression)commonSelector.Body);

        List<MemberBinding> newBindings = new List<MemberBinding>(zeroth.Bindings.OfType<MemberAssignment>());

        var spNestedType = typeof (InspectionTopicDataAllViewModel).GetProperty(nameof(SparseData));

        newBindings.Add(Expression.Bind(spNestedType, sInit));

        var newInit = Expression.MemberInit(xNew, newBindings);

        var childSelector = Expression.Lambda<Func<InspectionTopicDataAll, InspectionTopicDataAllViewModel>>(newInit, xParameter);

        return childSelector;
    } 

并将其用于替代下面的选择语句:

var model =
            worksheetService.GetInspectionTopicData(inspectionTopicId, inspectionId, discrimintor, value)
                .Select(GetCustomSelectExpression(columns));

你的方法 worksheetService.GetInspectionTopicData,是 Linq-To-Entities 方法还是 Linq-To-Objects 方法?它返回 IQueryable 还是 IEnumerable? - Shlomo
它返回一个IQueryable。 - mesut
1个回答

2
你的childSelector不知道commonSelectorx参数。在表达式树中,参数是由实例识别的,而不仅仅是名称,因此两个x参数实际上是不同的(即使在调试视图中它们看起来相同)。最简单的解决方法可能是更改你的
// input parameter "x"
var xParameter = Expression.Parameter(typeof(InspectionTopicDataAll), "x"); 

为了

var xParameter = commonSelector.Parameters[0]; 

那应该就可以解决问题了 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接