简体   繁体   English

将通用函数传递为 Linq 表达式 C#

[英]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实现ISoftDeleteExpression<>不是协变的) ,这就是为什么它不起作用。

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 并在ModelBuilderEntityTypeBuilder<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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM