按键表达存储静态filter
我有一个函数生成一个表达式来过滤一个表的主键,当它在Object[]
传递时,这与Find
函数非常相似,只是它没有实现,所以你可以在之后传递一个IQueryable
public static Expression<Func> FilterByPrimaryKeyPredicate(this DbContext dbContext, object[] id) { var keyProperties = dbContext.GetPrimaryKeyProperties(); var parameter = Expression.Parameter(typeof(T), "e"); var body = keyProperties // e => e.{propertyName} == new {id = id[i]}.id .Select((p, i) => Expression.Equal( Expression.Property(parameter, p.Name), Expression.Convert( Expression.PropertyOrField(Expression.Constant(new { id = id[i] }), "id"), p.ClrType))) .Aggregate(Expression.AndAlso); return Expression.Lambda<Func>(body, parameter); }
这首先获取表的主键,它创建二进制表达式foreach属性,Id以匿名类型包装以利用查询缓存。 这工作正常。 但是,我想更进一步。
我想保留Expression,所以每次传递一组新的id时我都不必生成它,如何在仍然利用查询缓存的同时存储此Expression
?
编辑TL; DR
所以我试图在静态类中使用数组访问来缓存它,如我所知,但是我遇到了一个错误:
public class PrimaryKeyFilterContainer { const string ANON_ID_PROP = "id"; static Expression<Func> _filter; Type ANON_TYPE = new { id = (object)0 }.GetType(); public object[] id { get; set; } public PrimaryKeyFilterContainer() { } public Expression<Func> GetFilter(DbContext dbContext, object[] id) { this.id = id; if(null == _filter) { var keyProperties = dbContext.GetPrimaryKeyProperties(); var parameter = Expression.Parameter(typeof(T), "e"); var body = keyProperties // e => e.PK[i] == id[i] .Select((p, i) => Expression.Equal( Expression.Property(parameter, p.Name), Expression.Convert(BuildNewExpression(i), p.ClrType))) .Aggregate(Expression.AndAlso); _filter = Expression.Lambda<Func>(body, parameter); } return _filter; } NewExpression BuildNewExpression(int index) { var currentObject = Expression.Constant(this); var fieldAccess = Expression.PropertyOrField(currentObject, nameof(id)); var arrayAccess = Expression.ArrayAccess(fieldAccess, Expression.Constant(index)); return Expression.New(ANON_TYPE.GetConstructor(new[] { typeof(object) }), arrayAccess); } }
类型’ f__AnonymousType0`1 [System.Object]’和’System.Int32’之间没有定义强制运算符
我越来越近但我不确定它是否会继续工作。
正如我在评论中提到的,主要问题是我们不能在表达式树中使用数组索引访问 – EF6抛出不支持的exception,EF Core将其转换为客户端评估。
因此,我们需要将键存储在具有动态计数属性和属性类型的类中。 幸运的是, System.Tuple
generics类提供了这样的function,并且可以在EF6和EF Core中使用。
以下是实现上述想法的类:
public class PrimaryKeyFilter where TEntity : class { object valueBuffer; Func (); // Create value buffer type (Tuple) from key properties var valueBufferType = TupleTypes[keyProperties.Count - 1] .MakeGenericType(keyProperties.Select(p => p.ClrType).ToArray()); // Build the delegate for converting value array to value buffer { // object[] values => new Tuple(values[0], values[1], ...) var parameter = Expression.Parameter(typeof(object[]), "values"); var body = Expression.New( valueBufferType.GetConstructors().Single(), keyProperties.Select((p, i) => Expression.Convert( Expression.ArrayIndex(parameter, Expression.Constant(i)), p.ClrType))); valueArrayConverter = Expression.Lambda>(body, parameter).Compile(); } // Build the predicate expression { var parameter = Expression.Parameter(typeof(TEntity), "e"); var valueBuffer = Expression.Convert( Expression.Field(Expression.Constant(this), nameof(this.valueBuffer)), valueBufferType); var body = keyProperties // e => e.{propertyName} == valueBuffer.Item{i + 1} .Select((p, i) => Expression.Equal( Expression.Property(parameter, p.Name), Expression.Property(valueBuffer, $"Item{i + 1}"))) .Aggregate(Expression.AndAlso); Predicate = Expression.Lambda>(body, parameter); } } public Expression> Predicate { get; } public void SetValues(params object[] values) => valueBuffer = valueArrayConverter(values); static readonly Type[] TupleTypes = { typeof(Tuple<>), typeof(Tuple<,>), typeof(Tuple<,,>), typeof(Tuple<,,,>), typeof(Tuple<,,,,>), typeof(Tuple<,,,,,>), typeof(Tuple<,,,,,,>), typeof(Tuple<,,,,,,,>), }; }
您可以创建和存储该类的实例。 然后使用查询中Predicate
属性返回的表达式。 和SetValues
方法一起设置参数。
缺点是值存储绑定到类实例,因此不能同时使用它。 最初的方法适用于所有场景,IMO的性能影响应该可以忽略不计,因此您可以考虑坚持下去。
- 将Env Conn字符串注入.NET Core 2.0 w / EF Core DbContext中的不同类lib比启动prj并实现IDesignTimeDbContextFactory
- 如何从.net核心中的appsettings.json中提取列表
- 将.NETFramework 4.5 dll添加到.NETCore项目中
- .Net Core MySql
- 以编程方式启动用于Selenium测试的.NET Core Web应用程序
- 在Entity Framework Core中创建迁移时如何配置DbContext?
- 有没有办法在.NET Core中只使用一个文件来运行控制台应用程序?
- ASP .NET CORE无法使用自定义程序集查找文件或程序集
- .NET Core IServiceScopeFactory.CreateScope()vs IServiceProvider.CreateScope()扩展