C# 如何向在服务器上执行的EFCore添加自定义代码
问题: 我使用的是EFCore 3.1(一个最近从旧版本迁移过来的项目),我必须根据记录是否被软删除来检索记录。对表执行的任何操作都不考虑任何软删除记录 软删除是以IsDeleted表格列的形式实现的,它是一个位。1=软删除,0=行可用。在C#中,有一个接口IActiveEntity,其bool属性为IsDeletedC# 如何向在服务器上执行的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
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))