Entity framework 在实体框架中构建堆栈
我模型中的一种实体类型(我们称之为E1)需要能够将其与另一种实体类型(E2)的关系视为堆栈。反过来,该另一实体需要能够看到E2位于堆栈顶部的第一种类型的所有相关实体,以及E2位于E1堆栈内的每种情况 我觉得这听起来不太清楚,所以让我试着证明: E1实体:foo{stack:quox,plugh,glurp},bar{stack:plugh,glurp},baz{stack:quox,plugh} E2实体:quux{top:Entity framework 在实体框架中构建堆栈,entity-framework,stack,Entity Framework,Stack,我模型中的一种实体类型(我们称之为E1)需要能够将其与另一种实体类型(E2)的关系视为堆栈。反过来,该另一实体需要能够看到E2位于堆栈顶部的第一种类型的所有相关实体,以及E2位于E1堆栈内的每种情况 我觉得这听起来不太清楚,所以让我试着证明: E1实体:foo{stack:quox,plugh,glurp},bar{stack:plugh,glurp},baz{stack:quox,plugh} E2实体:quux{top:null;in:foo,baz},plugh{top:baz;in:fo
null
;in:foo,baz},plugh{top:baz;in:foo,baz},glurp{top:bar;in:foo,bar}
现在,我有一个数据库表,其中包含E1和E2键的列,以及用于存储E2在堆栈中位置的int。实体框架将此表视为其自己的实体,而不是E1和E2之间关系的一部分,这会使查询复杂化,并导致一些非常难看的代码
我知道我做错了,但有可能做对吗?如果是这样,怎么做?因此,如果我们调用关系表R,每个E1“包含”多个R(它是堆栈,按R.position排序),每个E2以两种方式包含多个R:R.position==R.E1.top的和不包含的)
- 对吗
- 你是如何追踪最高层的?当堆栈发生变化时(例如E1.top为常数),您是否更新E1的所有Rs,还是将其存储在每个E1中
- 将Rs链接起来(给他们一个“下一个指针”而不是“位置”)有意义吗
- 您需要随机访问堆栈吗
- E2真的需要知道他们不在顶端的情况吗
- 因为这是一种非常奇特的情况,所以没有内置支持。但是你可以让它看起来很漂亮
现在,假设联接表名为E1E2,则会出现类似的情况
public partial class E1
{
public Guid Id { get; set; }
public IQueryable<E1E2> Stack { get; }
}
public partial class E2
{
public Guid Id { get; set; }
public IQueryable<E1E2> In { get; }
}
public partial class E1E2
{
public E1 E1 { get; set; }
public E2 E2 { get; set; }
public Int32 Position { get; set; }
}
公共部分类E1
{
公共Guid Id{get;set;}
公共IQueryable堆栈{get;}
}
公共部分类E2
{
公共Guid Id{get;set;}
{get;}中的公共IQueryable
}
公共部分类E1E2
{
公共e1e1{get;set;}
公共E2 E2{get;set;}
公共Int32位置{get;set;}
}
作为hoc,我无法想出更好的解决方案来将其映射到数据库。要使使用尽可能智能,只需向实体添加一些属性和方法。这很容易,因为实体a是作为分部类生成的
用如下内容扩展类E1
public partial class E1
{
public IQueryable<E2> NiceStack
{
get { return this.Stack.Select(s => s.E2).OrderBy(s => s.Position); }
}
public void Push(E2 e2)
{
this.Stack.Add(
new E1E2
{
E2 = e2,
Position = this.Stack.Max(s => s.Position) + 1
});
}
public E2 Pop()
{
return this.Stack.
Where(s => s.Position == this.Stack.Max(s => s.Position).
Select(s => s.E2).
Single();
}
}
公共部分类E1
{
公共IQueryable堆栈
{
获取{返回this.Stack.Select(s=>s.E2).OrderBy(s=>s.Position);}
}
公共无效推送(E2)
{
这个是.Stack.Add(
新E1E2
{
E2=E2,
Position=this.Stack.Max(s=>s.Position)+1
});
}
公共服务
{
返回这个.Stack。
其中(s=>s.Position==this.Stack.Max(s=>s.Position)。
选择(s=>s.E2)。
单个();
}
}
用如下内容扩展E2类
public partial class E2
{
public IQueryable<E1> NiceIn
{
get { return this.In.Select(i => i.E1); }
}
public IQueryable<E1> NiceTop
{
get
{
return this.In.
Where(i => i.Position == i.E1.Stack.Max(s => s.Position)).
Select(i => i.E1);
}
}
}
公共部分类E2
{
公共可查询信息
{
获取{返回this.In.Select(i=>i.E1);}
}
公共IQueryable NiceTop
{
得到
{
把这个还给我。
其中(i=>i.Position==i.E1.Stack.Max(s=>s.Position))。
选择(i=>i.E1);
}
}
}
到此为止。现在应该可以围绕这些实体编写非常好的代码了。代码中可能有一些bug,但想法应该很清楚。我省略了代码,以确保在访问时加载相关属性。您可以进一步将原始属性设为privat并从外部隐藏。也许您应该这样做不包括NiceStack属性,因为这允许随机访问。或者,您可能希望添加更多扩展-可能使NiceTop可写,将E2实例推送到插入E2实例NiceTop的E1实例的堆栈上。但想法保持不变
对Single()的调用将不适用于普通实体框架;而是使用ToList().Single()切换到LINQ to Object或使用First()但首先并不能保留一个的语义。现在每个E2只以一种方式包含多个R,并且通过LINQ查询确定top。我希望R完全不可见,E1s在关系的末尾有一个堆栈对象,E2s有两个列表——一个用于top,一个用于一般堆栈内。随机a只要知道E2在E1的堆栈中的某个位置,就只需要访问,这样,如果E2想要更改堆栈上的内容,它就可以这样做。(E2将E1委托给他们,并且可以将这些E1委托给其他E2,包括更改他们之前将E1委托给的人。)链接(我的理解是给每个R一个可为空的列来指向另一个R)可能也很好,但我担心可能会进一步增加问题的复杂性。OTOH更难搞错,这意味着我不再有拆分键了。(R的PK是(E1.键,位置)atm。)