为其中包含嵌套实体的实体生成动态 Select 表达式
Generating Dynamic Select Expression for entities with nested entity in it
我有一个 Class 有一个 属性 类型的另一个实体。如下所示:
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
{
}
我需要编写一个动态 Select 表达式,它能够 select 我想要的列。
我这样写:
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}}}
但是当我使用 运行 表达式时抛出了这个错误:
variable 'x' of type
'InternalControl.Domain.Entities.InspectionTopicDataAll'
referenced from scope '', but it is not defined
我不知道如何解决这个错误...
更新:感谢@MBoros 的帮助,对于他们以后可能需要这种类型的代码,使用上面的代码,即使代码可以工作并且可以从数据库中检索结果, select 语句没有按预期工作,所有列都从数据库中获取,所以为了解决这个问题,我使用了下面的代码:希望能帮助其他尝试做这种事情的人。
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;
}
并使用它代替 select 语句,如下所示:
var model =
worksheetService.GetInspectionTopicData(inspectionTopicId, inspectionId, discrimintor, value)
.Select(GetCustomSelectExpression(columns));
您的 childSelector
不知道 commonSelector
的 x
参数。在表达式树中,参数是按实例识别的,而不仅仅是按名称识别的,因此两个 x
参数实际上是不同的(即使在调试视图中它们看起来相同)。
最简单的解决方案可能是更改您的
// input parameter "x"
var xParameter = Expression.Parameter(typeof(InspectionTopicDataAll), "x");
到
var xParameter = commonSelector.Parameters[0];
这应该可以解决问题:)
我有一个 Class 有一个 属性 类型的另一个实体。如下所示:
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
{
}
我需要编写一个动态 Select 表达式,它能够 select 我想要的列。
我这样写:
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}}}
但是当我使用 运行 表达式时抛出了这个错误:
variable 'x' of type 'InternalControl.Domain.Entities.InspectionTopicDataAll' referenced from scope '', but it is not defined
我不知道如何解决这个错误...
更新:感谢@MBoros 的帮助,对于他们以后可能需要这种类型的代码,使用上面的代码,即使代码可以工作并且可以从数据库中检索结果, select 语句没有按预期工作,所有列都从数据库中获取,所以为了解决这个问题,我使用了下面的代码:希望能帮助其他尝试做这种事情的人。
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;
}
并使用它代替 select 语句,如下所示:
var model =
worksheetService.GetInspectionTopicData(inspectionTopicId, inspectionId, discrimintor, value)
.Select(GetCustomSelectExpression(columns));
您的 childSelector
不知道 commonSelector
的 x
参数。在表达式树中,参数是按实例识别的,而不仅仅是按名称识别的,因此两个 x
参数实际上是不同的(即使在调试视图中它们看起来相同)。
最简单的解决方案可能是更改您的
// input parameter "x"
var xParameter = Expression.Parameter(typeof(InspectionTopicDataAll), "x");
到
var xParameter = commonSelector.Parameters[0];
这应该可以解决问题:)