[英]Passing generic func as Linq expression C#
I have a Linq expression that is repeating over multiple places.我有一个在多个地方重复的 Linq 表达式。 I want to centrally define the Linq expression and use it in all such places.
我想集中定义 Linq 表达式并在所有这些地方使用它。 Here is the code:
这是代码:
public interface ISoftDelete
{
DateTime? DeletedOn { get; set; }
}
public class BaseModel : ISoftDelete
{
public int Id { get; set; }
public DateTime? DeletedOn { get; set; }
}
public class Epic: BaseModel {
}
public class Feature: BaseModel {
}
public class UserStory: BaseModel {
}
public class ProductFocusDbContext : DbContext
{
public ProductFocusDbContext(DbContextOptions<ProductFocusDbContext> options) : base(options) { }
public DbSet<Epic> Epics { get; set; }
public DbSet<Feature> Features { get; set; }
public DbSet<UserStory> UserStories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Epic
modelBuilder.Entity<Epic>().HasQueryFilter(ExcludeSoftDeleted);
// Feature
modelBuilder.Entity<Feature>().HasQueryFilter(x => x.DeletedOn == null);
// User Story
modelBuilder.Entity<UserStory>().HasQueryFilter(x => x.DeletedOn == null);
}
System.Linq.Expressions.Expression<Func<ISoftDelete, bool>> ExcludeSoftDeleted = (x => x.DeletedOn == null) ;
}
I want to replace all occurrences of x => x.DeletedOn == null
with ExcludeSoftDeleted, but I get following exception when I try do it using the above code:我想用 ExcludeSoftDeleted 替换所有出现的
x => x.DeletedOn == null
,但是当我尝试使用上面的代码执行此操作时出现以下异常:
InvalidOperationException: The filter expression 'x => (x.DeletedOn == null)' specified for entity type 'Epic' is invalid.
InvalidOperationException:为实体类型“Epic”指定的过滤器表达式“x => (x.DeletedOn == null)”无效。 The expression must accept a single parameter of type 'ProductFocus.Domain.Model.Epic', return bool, and may not contain references to navigation properties.
表达式必须接受“ProductFocus.Domain.Model.Epic”类型的单个参数,返回布尔值,并且不得包含对导航属性的引用。
How can I achieve it?我怎样才能实现它?
HasQueryFilter
is a generic method where the generic parameter T
matches that of the previous call to Entity<EntityType>
. HasQueryFilter
是一个泛型方法,其中泛型参数T
与之前对Entity<EntityType>
的调用相匹配。 You have no problem passing an expression manually as the appropriate types get used.当使用适当的类型时,手动传递表达式没有问题。 However, the expression property you tried passing is of type
Expression<Func<ISoftDelete, bool>>
and there is no implicit conversion from Expression<Func<EntityType, bool>>
, even if EntityType
implements ISoftDelete
( Expression<>
is not covariant), which is why it does not work.但是,您尝试传递的表达式属性的类型是
Expression<Func<ISoftDelete, bool>>
并且没有从Expression<Func<EntityType, bool>>
的隐式转换,即使EntityType
实现ISoftDelete
( Expression<>
不是协变的) ,这就是为什么它不起作用。
You can get around this by providing some helper classes that can return you the appropriate expression for your entity.你可以通过提供一些帮助类来解决这个问题,这些类可以为你的实体返回适当的表达式。
public static class SoftDeleteHelper<T>
where T: ISoftDelete // constrain generic type to interface
{
public static Expression<Func<T, bool>> ExcludeSoftDeleted
=> (x => x.DeletedOn == null):
}
And then you can refer to this inside of your query filter:然后您可以在查询过滤器中引用它:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Epic>().HasQueryFilter(SoftDeleteHelper<Epic>.ExcludeSoftDeleted);
modelBuilder.Entity<Feature>().HasQueryFilter(SoftDeleteHelper<Feature>.ExcludeSoftDeleted);
modelBuilder.Entity<UserStory>().HasQueryFilter(SoftDeleteHelper<UserStory>.ExcludeSoftDeleted);
}
The key here is that we've constrained the generic parameter to ISoftDelete
which guarantees that the DeletedOn
property exists on the entity type.这里的关键是我们将泛型参数限制为
ISoftDelete
,这保证了实体类型上存在DeletedOn
属性。
Alternatively you could define this as a method that returns your entity expression.或者,您可以将其定义为返回实体表达式的方法。 This may be more suitable if you have other query filters that need to be constrained to different interfaces:
如果您有其他需要约束到不同接口的查询过滤器,这可能更合适:
public static class ExpressionHelper
{
public static Expression<Func<T, bool>> ExcludeSoftDeleted<T>()
where T: ISoftDelete // constrained to interface
=> (x => x.DeletedOn == null);
}
Which can then be used like the following (note this is different from above and requires ()
as you are invoking a function that returns the expression and not referencing a property)然后可以像下面这样使用它(注意这与上面不同,需要
()
,因为您正在调用返回表达式而不是引用属性的 function)
modelBuilder.Entity<Epic>().HasQueryFilter(ExpressionHelper.ExcludeSoftDeleted<Epic>())
You could go a step further and write extension methods on ModelBuilder
or EntityTypeBuilder<T>
constrained to the generic type and omit the helper class altogether您可以进一步 go 并在
ModelBuilder
或EntityTypeBuilder<T>
上编写扩展方法,限制为泛型类型,并完全省略帮助器 class
public static class EntityBuilderExtensions
{
// extension method on the main builder
public static EntityTypeBuilder<T> EntityWithSoftDelete<T>(
this ModelBuilder builder)
where T: class, ISoftDelete // extra class constraint required by Entity<>
{
return builder.Entity<T>().WithSoftDelete();
}
// extension method on the result of Entity<T>
public static EntityTypeBuilder<T> WithSoftDelete<T>(
this EntityTypeBuilder<T> builder)
where T: class, ISoftDelete // extra class constraint required by Entity<>
{
return builder.HasQueryFilter(
e => e.DeletedOn == null
);
}
}
This works once again thanks to the generic constraint on ISoftDelete
.由于
ISoftDelete
上的通用约束,这再次起作用。 You can then call them like:然后你可以这样称呼他们:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Epic>().WithSoftDelete();
// or
modelBuilder.EntityWithSoftDelete<Feature>();
}
The methods return an EntityTypeBuilder<T>
which you can then use to chain further entity configurations.这些方法返回一个
EntityTypeBuilder<T>
,然后您可以使用它来链接更多的实体配置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.