C# 如何向在服务器上执行的EFCore添加自定义代码

C# 如何向在服务器上执行的EFCore添加自定义代码,c#,entity-framework,ef-core-3.1,C#,Entity Framework,Ef Core 3.1,问题: 我使用的是EFCore 3.1(一个最近从旧版本迁移过来的项目),我必须根据记录是否被软删除来检索记录。对表执行的任何操作都不考虑任何软删除记录 软删除是以IsDeleted表格列的形式实现的,它是一个位。1=软删除,0=行可用。在C#中,有一个接口IActiveEntity,其bool属性为IsDeleted interface IActiveEntity { bool IsDeleted { get; set; } } 某些操作以泛型形式实现,因此它们检查实体是否为IAc

问题: 我使用的是EFCore 3.1(一个最近从旧版本迁移过来的项目),我必须根据记录是否被软删除来检索记录。对表执行的任何操作都不考虑任何软删除记录

软删除是以IsDeleted表格列的形式实现的,它是一个位。1=软删除,0=行可用。在C#中,有一个接口IActiveEntity,其bool属性为IsDeleted

interface IActiveEntity
{ 
    bool IsDeleted { get; set; }
}
某些操作以泛型形式实现,因此它们检查实体是否为IActiveEntity类型。我读了一系列文章

但它并不像文章中提到的那样工作。也没有足够的可用文档

我为EF实现了函数扩展。函数:

public static class FunctionsExtension
    {
       public static MethodInfo GetIsDeletedMethodInfo()
       {
          var  methods = typeof(FunctionsExtension)
             .GetMethods()
             .Where(x=> x.Name == "IsDeleted" && !x.GetParameters().Any())
             .ToList();
          
          return methods.FirstOrDefault();
       }
       
        public static bool IsDeleted(this DbFunctions sys, object entity)
        {
           throw new InvalidOperationException("This method is for use with Entity Framework Core only and has no in-memory implementation.");
        }
       
        public static bool IsDeleted()
        {
           throw new InvalidOperationException("This method is for use with Entity Framework Core only and has no in-memory implementation.");
        }
    }
然后我创建了相应的类来将表达式转换为正确的sql代码

    public class IsDeletedExpression : SqlExpression
    {
       public IsDeletedExpression() : base(typeof(object), new BoolTypeMapping("MSSQLSERVER", DbType.Boolean))
       {
       }

      protected override Expression Accept(ExpressionVisitor visitor)
      {
         if(visitor is IQuerySqlGeneratorFactory fac)
         {
            var gen = fac.Create();
            gen.Visit(new SqlFragmentExpression("([DeletedOn] IS NULL)"));

            return this;
         }

         return base.Accept(visitor);
      }
      public override bool CanReduce => false;
      public override void Print(ExpressionPrinter printer) => printer.Print(this);
      protected override Expression VisitChildren(ExpressionVisitor visitor)=> base.VisitChildren(visitor);
      
    }
然后,IsDeleted表达式的方法调用转换器:

    public class IsDeletedTranslator : IMethodCallTranslator
    {
        public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments)
        {
         if(method.Name == "IsDeleted")
            return new IsDeletedExpression();

         return null;
        }
    }
这是我得到的错误:

LINQ表达式

    .Where(n => __Functions_0
        .IsDeleted(n))

无法翻译。以可以翻译的形式重写查询,或者通过插入对AsEnumerable()、AsAsAsAsyncEnumerable()、ToList()或ToListSync()的调用显式切换到客户端计算。有关更多信息,请参阅。

以及您实际如何使用IsDeleted方法?上面似乎缺少此代码。我刚刚更新了我的问题以包含一个示例。让我们假设
EF.Functions.IsDeleted()
工作正常。过滤将仅应用于主实体(员工),而不是通过
Include
Select
包含的实体,这可能不是所需的行为。这很好,但问题是,这不会按预期工作,也不会发生SQL转换
public sealed class MyContextOptionsExtension : IDbContextOptionsExtension
    {
       public void ApplyServices(IServiceCollection services) => services.AddSingleton<IMethodCallTranslatorPlugin, IsDeletedMethodCallTranslatorPlugin>();
       public void Validate(IDbContextOptions options){}
       public DbContextOptionsExtensionInfo Info => new MyContextExtensionInfo(this);
    }

    public sealed class MyContextExtensionInfo : DbContextOptionsExtensionInfo
    {
        public MyContextExtensionInfo(IDbContextOptionsExtension extension) : base(extension){}
        public override long GetServiceProviderHashCode() => 0;
        public override bool IsDatabaseProvider => true;
        public override string LogFragment => "([DeletedOn] IS NULL)";       
        public override void PopulateDebugInfo(IDictionary<string, string> debugInfo){}
    }
    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder
        .HasDbFunction(FunctionsExtension.GetIsDeletedMethodInfo())
        .HasTranslation((list) =>  new IsDeletedExpression());
    }


        public static MyContext GetInstance(string cnn)
        {
            var optionsBuilder = new DbContextOptionsBuilder<MyContext>();
            optionsBuilder.UseSqlServer(cnn, cb =>
            {
            });

            var extension = optionsBuilder.Options.FindExtension<MyContextOptionsExtension>() ?? new MyContextOptionsExtension();
            optionsBuilder.Options.WithExtension(extension);
            var context = new MyContext(optionsBuilder.Options);

            return context;
        }
  var maxAge = employees.Where(x => !EF.Functions.IsDeleted(x)).Max(x=> x.Age)
    .Where(n => __Functions_0
        .IsDeleted(n))