- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
基本上,我想实现一个存储库,它甚至可以通过导航属性过滤所有软删除的记录。所以我有一个基本实体,类似这样的东西:
public abstract class Entity
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
...
}
还有一个存储库:
public class BaseStore<TEntity> : IStore<TEntity> where TEntity : Entity
{
protected readonly ApplicationDbContext db;
public IQueryable<TEntity> GetAll()
{
return db.Set<TEntity>().Where(e => !e.IsDeleted)
.InterceptWith(new InjectConditionVisitor<Entity>(entity => !entity.IsDeleted));
}
public IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate)
{
return GetAll().Where(predicate);
}
public IQueryable<TEntity> GetAllWithDeleted()
{
return db.Set<TEntity>();
}
...
}
InterceptWith 函数来自这个项目:https://github.com/davidfowl/QueryInterceptor和 https://github.com/StefH/QueryInterceptor (与异步实现相同)
IStore<Project>
的用法看起来像:
var project = await ProjectStore.GetAll()
.Include(p => p.Versions).SingleOrDefaultAsync(p => p.Id == projectId);
我实现了一个 ExpressionVisitor:
internal class InjectConditionVisitor<T> : ExpressionVisitor
{
private Expression<Func<T, bool>> queryCondition;
public InjectConditionVisitor(Expression<Func<T, bool>> condition)
{
queryCondition = condition;
}
public override Expression Visit(Expression node)
{
return base.Visit(node);
}
}
但这就是我卡住的地方。我在 Visit 函数中放置了一个断点,以查看我得到了什么表达式,以及我什么时候应该做一些厚颜无耻的事情,但它永远不会到达我的树的 Include(p => p.Versions) 部分。
我看到了一些其他可能有效的解决方案,但那些是“永久的”,例如 EntityFramework.Filters似乎适用于大多数用例,但您必须在配置 DbContext 时添加过滤器 - 但是,您可以禁用过滤器,但我不想为每个查询禁用和重新启用过滤器。另一个类似的解决方案是订阅 ObjectContext 的 ObjectMaterialized 事件,但我也不喜欢它。
我的目标是“捕获”访问者中的包含项并修改表达式树以向连接添加另一个条件,该条件仅在您使用商店的 GetAll 函数之一时才检查记录的 IsDeleted 字段。任何帮助将不胜感激!
更新
我的存储库的目的是隐藏基础实体的一些基本行为——它还包含“创建/上次修改者”、“创建/上次修改日期”、时间戳等。我的 BLL 通过这个存储库获取所有数据所以它不需要担心这些,商店会处理所有的事情。也有可能从 BaseStore
继承。对于特定的类(然后我配置的 DI 会将继承的类注入(inject)到 IStore<Project>
如果它存在),您可以在其中添加特定的行为。比如你修改了一个项目,你需要添加这些修改历史,那么你就把这个添加到继承store的update函数中即可。
当您查询具有导航属性的类(所以任何类 :D )时,问题就开始了。有两个具体实体:
public class Project : Entity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Platform> Platforms { get; set; }
//note: this version is not historical data, just the versions of the project, like: 1.0.0, 1.4.2, 2.1.0, etc.
public virtual ICollection<ProjectVersion> Versions { get; set; }
}
public class Platform : Entity
{
public string Name { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection<TestFunction> TestFunctions { get; set; }
}
public class ProjectVersion : Entity
{
public string Code { get; set; }
public virtual Project Project { get; set; }
}
所以如果我想列出项目的版本,我调用商店:await ProjectStore.GetAll().Include(p => p.Versions).SingleOrDefaultAsync(p => p.Id == projectId)
.我不会得到删除的项目,但如果项目存在,它会返回所有相关的版本,包括删除的版本。在这种特定情况下,我可以从另一侧开始并调用 ProjectVersionStore,但如果我想通过 2+ 个导航属性进行查询,那么游戏就结束了:)</p>
预期的行为是:如果我将版本包含到项目中,它应该只查询未删除的版本 - 所以生成的 sql 连接应该包含 [Versions].[IsDeleted] = FALSE
条件也。对于像 Include(project => project.Platforms.Select(platform => platform.TestFunctions))
这样的复杂包含,它甚至更加复杂。 .
我尝试这样做的原因是我不想将 BLL 中的所有 Include 重构为其他内容。那是懒惰的部分:)另一个是我想要一个透明的解决方案,我不希望 BLL 知道所有这些。如果不是绝对必要,接口(interface)应该保持不变。我知道这只是一个扩展方法,但这种行为应该在商店层。
最佳答案
您使用的 include 方法调用方法 QueryableExtensions.Include(source, path1) 将表达式转换为字符串路径。这就是 include 方法的作用:
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path)
{
Check.NotNull<IQueryable<T>>(source, "source");
Check.NotNull<Expression<Func<T, TProperty>>>(path, "path");
string path1;
if (!DbHelpers.TryParsePath(path.Body, out path1) || path1 == null)
throw new ArgumentException(Strings.DbExtensions_InvalidIncludePathExpression, "path");
return QueryableExtensions.Include<T>(source, path1);
}
因此,您的表达式看起来像这样(检查表达式中的“Include”或“IncludeSpan”方法):
value(System.Data.Entity.Core.Objects.ObjectQuery`1[TEntity]).MergeAs(AppendOnly)
.IncludeSpan(value(System.Data.Entity.Core.Objects.Span))
您应该 Hook VisitMethodCall 来添加您的表达式:
internal class InjectConditionVisitor<T> : ExpressionVisitor
{
private Expression<Func<T, bool>> queryCondition;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Expression expression = node;
if (node.Method.Name == "Include" || node.Method.Name == "IncludeSpan")
{
// DO something here! Let just add an OrderBy for fun
// LAMBDA: x => x.[PropertyName]
var parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, "ColumnInt");
var lambda = Expression.Lambda(property, parameter);
// EXPRESSION: expression.[OrderMethod](x => x.[PropertyName])
var orderByMethod = typeof(Queryable).GetMethods().First(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
var orderByMethodGeneric = orderByMethod.MakeGenericMethod(typeof(T), property.Type);
expression = Expression.Call(null, orderByMethodGeneric, new[] { expression, Expression.Quote(lambda) });
}
else
{
expression = base.VisitMethodCall(node);
}
return expression;
}
}
David Fowl 的 QueryInterceptor 项目不支持“Include”。 Entity Framework 尝试使用反射查找“Include”方法,如果未找到(就是这种情况)则返回当前查询。
免责声明:我是项目的所有者EF+ .
我添加了一个支持“包含”的 QueryInterceptor 功能来回答您的问题。该功能尚不可用,因为尚未添加单元测试,但您可以下载并试用源代码:Query Interceptor Source
如果您有任何问题,请直接与我联系(电子邮件位于我的 GitHub 主页底部),否则这将开始偏离主题。
请注意,“Include”方法通过隐藏一些先前的表达式来修改表达式。因此,有时很难理解引擎盖下到底发生了什么。
我的项目还包含一个查询过滤器功能,我认为它具有更大的灵 active 。
编辑:根据更新的要求添加工作示例
这是您可以用于您的要求的起始代码:
public IQueryable<TEntity> GetAll()
{
var conditionVisitor = new InjectConditionVisitor<TEntity>("Versions", db.Set<TEntity>.Provider, x => x.Where(y => !y.IsDeleted));
return db.Set<TEntity>().Where(e => !e.IsDeleted).InterceptWith(conditionVisitor);
}
var project = await ProjectStore.GetAll().Include(p => p.Versions).SingleOrDefaultAsync(p => p.Id == projectId);
internal class InjectConditionVisitor<T> : ExpressionVisitor
{
private readonly string NavigationString;
private readonly IQueryProvider Provider;
private readonly Func<IQueryable<T>, IQueryable<T>> QueryCondition;
public InjectConditionVisitor(string navigationString, IQueryProvider provder , Func<IQueryable<T>, IQueryable<T>> queryCondition)
{
NavigationString = navigationString;
Provider = provder;
QueryCondition = queryCondition;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Expression expression = node;
bool isIncludeSpanValid = false;
if (node.Method.Name == "IncludeSpan")
{
var spanValue = (node.Arguments[0] as ConstantExpression).Value;
// The System.Data.Entity.Core.Objects.Span class and SpanList is internal, let play with reflection!
var spanListProperty = spanValue.GetType().GetProperty("SpanList", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var spanList = (IEnumerable)spanListProperty.GetValue(spanValue);
foreach (var span in spanList)
{
var spanNavigationsField = span.GetType().GetField("Navigations", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var spanNavigation = (List<string>)spanNavigationsField.GetValue(span);
if (spanNavigation.Contains(NavigationString))
{
isIncludeSpanValid = true;
break;
}
}
}
if ((node.Method.Name == "Include" && (node.Arguments[0] as ConstantExpression).Value.ToString() == NavigationString)
|| isIncludeSpanValid)
{
// CREATE a query from current expression
var query = Provider.CreateQuery<T>(expression);
// APPLY the query condition
query = QueryCondition(query);
// CHANGE the query expression
expression = query.Expression;
}
else
{
expression = base.VisitMethodCall(node);
}
return expression;
}
}
编辑:回答子问题
Include 和 IncludeSpan 的区别
据我了解
IncludeSpan:当原始查询尚未被 LINQ 方法修改时出现。
Include:当原始查询已被 LINQ 方法修改时出现(您不再看到以前的表达式)
-- Expression: {value(System.Data.Entity.Core.Objects.ObjectQuery`1[Z.Test.EntityFramework.Plus.Association_Multi_OneToMany_Left]).MergeAs(AppendOnly).IncludeSpan(value(System.Data.Entity.Core.Objects.Span))}
var q = ctx.Association_Multi_OneToMany_Lefts.Include(x => x.Right1s).Include(x => x.Right2s);
-- Expression: {value(System.Data.Entity.Core.Objects.ObjectQuery`1[Z.Test.EntityFramework.Plus.Association_Multi_OneToMany_Left]).Include("Right2s")}
var q = ctx.Association_Multi_OneToMany_Lefts.Include(x => x.Right1s).Where(x => x.ColumnInt > 10).Include(x => x.Right2s);
如何包含和过滤相关实体
Include 不允许您过滤相关实体。您可以在这篇文章中找到 2 个解决方案:EF. How to include only some sub results in a model?
关于c# - 修改 IQueryable.Include() 的表达式树,为 join 添加条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34971376/
关于 B 树与 B+ 树,网上有一个比较经典的问题:为什么 MongoDb 使用 B 树,而 MySQL 索引使用 B+ 树? 但实际上 MongoDb 真的用的是 B 树吗?
如何将 R* Tree 实现为持久(基于磁盘)树?保存 R* 树索引或保存叶值的文件的体系结构是什么? 注意:此外,如何在这种持久性 R* 树中执行插入、更新和删除操作? 注意事项二:我已经实现了一个
目前,我正在努力用 Java 表示我用 SML 编写的 AST 树,这样我就可以随时用 Java 遍历它。 我想知道是否应该在 Java 中创建一个 Node 类,其中包含我想要表示的数据,以及一个数
我之前用过这个库http://www.cs.umd.edu/~mount/ANN/ .但是,它们不提供范围查询实现。我猜是否有一个 C++ 范围查询实现(圆形或矩形),用于查询二维数据。 谢谢。 最佳
在进一步分析为什么MySQL数据库索引选择使用B+树之前,我相信很多小伙伴对数据结构中的树还是有些许模糊的,因此我们由浅入深一步步探讨树的演进过程,在一步步引出B树以及为什么MySQL数据库索引选择
书接上回,今天和大家一起动手来自己实现树。 相信通过前面的章节学习,大家已经明白树是什么了,今天我们主要针对二叉树,分别使用顺序存储和链式存储来实现树。 01、数组实现 我们在上一节中说过,
书节上回,我们接着聊二叉树,N叉树,以及树的存储。 01、满二叉树 如果一个二叉树,除最后一层节点外,每一层的节点数都达到最大值,即每个节点都有两个子节点,同时所有叶子节点都在最后一层,则这个
树是一种非线性数据结构,是以分支关系定义的层次结构,因此形态上和自然界中的倒挂的树很像,而数据结构中树根向上树叶向下。 什么是树? 01、定义 树是由n(n>=0)个元素节点组成的
操作系统的那棵“树” 今天从一颗 开始,我们看看如何从小树苗长成一颗苍天大树。 运转CPU CPU运转起来很简单,就是不断的从内存取值执行。 CPU没有好好运转 IO是个耗费时间的活,如果CPU在取值
我想为海洋生物学类(class)制作一个简单的系统发育树作为教育示例。我有一个具有分类等级的物种列表: Group <- c("Benthos","Benthos","Benthos","Be
我从这段代码中删除节点时遇到问题,如果我插入数字 12 并尝试删除它,它不会删除它,我尝试调试,似乎当它尝试删除时,它出错了树的。但是,如果我尝试删除它已经插入主节点的节点,它将删除它,或者我插入数字
B+ 树的叶节点链接在一起。将 B+ 树的指针结构视为有向图,它不是循环的。但是忽略指针的方向并将其视为链接在一起的无向叶节点会在图中创建循环。 在 Haskell 中,如何将叶子构造为父内部节点的子
我在 GWT 中使用树控件。我有一个自定义小部件,我将其添加为 TreeItem: Tree testTree = new Tree(); testTree.addItem(myWidget); 我想
它有点像混合树/链表结构。这是我定义结构的方式 struct node { nodeP sibling; nodeP child; nodeP parent; char
我编写了使用队列遍历树的代码,但是下面的出队函数生成错误,head = p->next 是否有问题?我不明白为什么这部分是错误的。 void Levelorder(void) { node *tmp,
例如,我想解析以下数组: var array1 = ["a.b.c.d", "a.e.f.g", "a.h", "a.i.j", "a.b.k"] 进入: var json1 = { "nod
问题 -> 给定一棵二叉树和一个和,确定该树是否具有从根到叶的路径,使得沿路径的所有值相加等于给定的和。 我的解决方案 -> public class Solution { public bo
我有一个创建 java 树的任务,它包含三列:运动名称、运动类别中的运动计数和上次更新。类似的东西显示在下面的图像上: 如您所见,有 4 种运动:水上运动、球类运动、跳伞运动和舞蹈运动。当我展开 sk
我想在 H2 数据库中实现 B+ Tree,但我想知道,B+ Tree 功能在 H2 数据库中可用吗? 最佳答案 H2 已经使用了 B+ 树(PageBtree 类)。 关于mysql - H2数据库
假设我们有 5 个字符串数组: String[] array1 = {"hello", "i", "cat"}; String[] array2 = {"hello", "i", "am"}; Str
我是一名优秀的程序员,十分优秀!