访问表达式主体成员以构建表达式树
Accessing expression bodied members to build expression trees
正在尝试使用表达式树按表达式构建顺序。但是我无法访问查询结果 class 的表达式 属性。
这是 class 结构:
public class AssetFileRecord : IAuditable, IEntity, INavigateToCustomValues
{
public AssetFileRecord()
{
this.UpdatedTimeStamp = DateTime.UtcNow;
}
public AssetFileRecord GetRecord()
{
return this;
}
public Guid Id { get; set; }
public int DisplayId { get; set; }
public string AssetTagNumber { get; set; }
[JObjectIgnore]
public virtual Account Account { get; set; }
public string AccountNumber => Account?.AccountNumber;
public string AuditTrail { get; set; }
public string OldTagNumber { get; set; }
public ActivityCode ActivityCode { get; set; }
[JObjectIgnore]
public virtual ICollection<AssetFileRecordDepreciation> AssetFileRecordDepreciations { get; set; }
// Depreciation Records
public double? AccumulatedDepreciation => Depreciation()?.AccumulatedDepreciation;
public DateTime? DepreciationAsOfDate => Depreciation()?.DepreciationAsOfDate;
public double? LifeMonths => Depreciation()?.LifeMonths;
public double? DepreciationBasis => Depreciation()?.DepreciationBasis;
public double? PeriodDepreciation => Depreciation()?.PeriodDepreciation;
private AssetFileRecordDepreciation Depreciation()
{
return AssetFileRecordDepreciations?.AsQueryable()?.OrderBy(d => d.AssetFileDepreciationBook.BookNo)?.FirstOrDefault();
}
}
我无法访问 属性 AccountNumber,它是 AssetFileRecord 的虚拟 属性 的 属性。
以下是适用于任何其他非表达式主体属性的当前代码。
var type = typeof(T);
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new[] { type, property.PropertyType };
var methodBase = isFirstOrderTerm ? "OrderBy" : "ThenBy";
var methodName = sortOrder == ListSortDirection.Ascending ? methodBase : $"{methodBase}Descending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
Expression.Call 未评估为有效的 SQL 查询,而是抛出异常。
((System.Data.Entity.Infrastructure.DbQuery<AssetFileRecord>)records).Sql = '((System.Data.Entity.Infrastructure.DbQuery<AssetFileRecord>)records).Sql' threw an exception of type 'System.NotSupportedException'
预期结果:最后生成的表达式树应该追加一个order by expression;虽然它没有这样做,但当试图通过表达式 bodied 属性 成员进行排序时。
谁能帮我解决这个问题。
你的方法有两个问题。首先是您不能在 Linq 表达式中使用 null 传播运算符。测试这段代码:
var account = new Account();
// will cause "error CS8072: An expression tree lambda may not contain a null propagating operator"
Expression<Func<string>> accountNumber = () => account?.AccountNumber;
第二个也是主要问题是您的 AccountNumber
将被编译成 get_AccountNumber
方法,您不能使用 Linq to SQL 调用任意方法。您可以测试此代码:
public class AssetFileRecord
{
//...
public string AccountNumber => Account != null ? Account.AccountNumber : null;
}
虽然可以编译,但它会产生相同的运行时异常。
解决此问题的一种可能方法是创建一个包含复杂属性表达式的地图:
var map = new Dictionary<string, Expression>
{
{
"AssetFileRecord.AccountNumber", // type and property
(Expression<Func<AssetFileRecord, string>>) (
afr => afr.Account != null ? afr.Account.AccountNumber : null
)
}
};
现在你可以重写你的方法来构建关于这张地图的动态OrderBy
:
private static IQueryable<T> DynamicOrderBy<T>(
IQueryable<T> source,
string sortProperty,
Dictionary<string, Expression> map)
{
var type = typeof(T);
var parameter = Expression.Parameter(type, "p");
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Expression whereLambda;
if (!map.TryGetValue($"{type.Name}.{sortProperty}", out whereLambda))
{
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
whereLambda = Expression.Lambda(propertyAccess, parameter);
}
// else we just using a lambda from map
// call OrderBy
var query = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] {type, property.PropertyType},
source.Expression,
whereLambda
);
return source.Provider.CreateQuery<T>(query);
}
正在尝试使用表达式树按表达式构建顺序。但是我无法访问查询结果 class 的表达式 属性。 这是 class 结构:
public class AssetFileRecord : IAuditable, IEntity, INavigateToCustomValues
{
public AssetFileRecord()
{
this.UpdatedTimeStamp = DateTime.UtcNow;
}
public AssetFileRecord GetRecord()
{
return this;
}
public Guid Id { get; set; }
public int DisplayId { get; set; }
public string AssetTagNumber { get; set; }
[JObjectIgnore]
public virtual Account Account { get; set; }
public string AccountNumber => Account?.AccountNumber;
public string AuditTrail { get; set; }
public string OldTagNumber { get; set; }
public ActivityCode ActivityCode { get; set; }
[JObjectIgnore]
public virtual ICollection<AssetFileRecordDepreciation> AssetFileRecordDepreciations { get; set; }
// Depreciation Records
public double? AccumulatedDepreciation => Depreciation()?.AccumulatedDepreciation;
public DateTime? DepreciationAsOfDate => Depreciation()?.DepreciationAsOfDate;
public double? LifeMonths => Depreciation()?.LifeMonths;
public double? DepreciationBasis => Depreciation()?.DepreciationBasis;
public double? PeriodDepreciation => Depreciation()?.PeriodDepreciation;
private AssetFileRecordDepreciation Depreciation()
{
return AssetFileRecordDepreciations?.AsQueryable()?.OrderBy(d => d.AssetFileDepreciationBook.BookNo)?.FirstOrDefault();
}
}
我无法访问 属性 AccountNumber,它是 AssetFileRecord 的虚拟 属性 的 属性。
以下是适用于任何其他非表达式主体属性的当前代码。
var type = typeof(T);
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new[] { type, property.PropertyType };
var methodBase = isFirstOrderTerm ? "OrderBy" : "ThenBy";
var methodName = sortOrder == ListSortDirection.Ascending ? methodBase : $"{methodBase}Descending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
Expression.Call 未评估为有效的 SQL 查询,而是抛出异常。
((System.Data.Entity.Infrastructure.DbQuery<AssetFileRecord>)records).Sql = '((System.Data.Entity.Infrastructure.DbQuery<AssetFileRecord>)records).Sql' threw an exception of type 'System.NotSupportedException'
预期结果:最后生成的表达式树应该追加一个order by expression;虽然它没有这样做,但当试图通过表达式 bodied 属性 成员进行排序时。
谁能帮我解决这个问题。
你的方法有两个问题。首先是您不能在 Linq 表达式中使用 null 传播运算符。测试这段代码:
var account = new Account();
// will cause "error CS8072: An expression tree lambda may not contain a null propagating operator"
Expression<Func<string>> accountNumber = () => account?.AccountNumber;
第二个也是主要问题是您的 AccountNumber
将被编译成 get_AccountNumber
方法,您不能使用 Linq to SQL 调用任意方法。您可以测试此代码:
public class AssetFileRecord
{
//...
public string AccountNumber => Account != null ? Account.AccountNumber : null;
}
虽然可以编译,但它会产生相同的运行时异常。
解决此问题的一种可能方法是创建一个包含复杂属性表达式的地图:
var map = new Dictionary<string, Expression>
{
{
"AssetFileRecord.AccountNumber", // type and property
(Expression<Func<AssetFileRecord, string>>) (
afr => afr.Account != null ? afr.Account.AccountNumber : null
)
}
};
现在你可以重写你的方法来构建关于这张地图的动态OrderBy
:
private static IQueryable<T> DynamicOrderBy<T>(
IQueryable<T> source,
string sortProperty,
Dictionary<string, Expression> map)
{
var type = typeof(T);
var parameter = Expression.Parameter(type, "p");
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Expression whereLambda;
if (!map.TryGetValue($"{type.Name}.{sortProperty}", out whereLambda))
{
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
whereLambda = Expression.Lambda(propertyAccess, parameter);
}
// else we just using a lambda from map
// call OrderBy
var query = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] {type, property.PropertyType},
source.Expression,
whereLambda
);
return source.Provider.CreateQuery<T>(query);
}