c# - LINQ to Entities:对多对多关系执行连接(代码优先)
问题描述
我有以下模型:
[Table("Experiments")]
public class Experiment
{
...
public virtual ICollection<ExperimentType> ExperimentTypes { get; set; }
public Experiment()
{
ExperimentTypes = new List<ExperimentType>();
}
}
[Table("ExperimentTypes")]
public class ExperimentType
{
...
public virtual ICollection<Experiment> Experiments { get; set; }
public ExperimentType()
{
Experiments = new List<Experiments>();
}
}
DbSet 包含:
public DbSet<Experiment> Experiments { get; set; }
public DbSet<ExperimentType> ExperimentTypes{ get; set; }
这会在 SQL 上创建一个名为 ExperimentExperimentTypes 的表。
现在,我想执行 LINQ 连接,例如:
var query =
from e in database.Experiments
join eet in database.ExperimentExperimentTypes on eet.Experiment_Id equals eet.ExperimentType_Id ...
但显然 database.ExperimentExperimentTypes 在代码中无法识别。
为了告诉代码有这个表,我尝试了很多东西,我也尝试创建相应的c#类,但没有得到任何结果。
怎样才能做到这一点?
解决方案
所以你有两个表:Experiment
和ExperimentType
. 这两者之间存在多对多的关系:每Experiment
一个都是零个或多个实验ExperimentTypes
;everyExperimentType
是 zero 或 more 的类型Experiments
。
这种多对多可以在您的类定义中看到。两边的virtual ICollection<...>
表示多对多的关系。
在关系数据库中,这种多对多关系是使用联结表实现的。但是,在实体框架中,您很少看到联结表。它在您的数据库中,但是,您无法使用 DbContext 访问它
但是,如果我无法访问联结表,我将如何在 Experiments 和 ExperimentTypes 之间执行联结呢?
好吧,小熊维尼应该回到他的思考点。你不想加入表,你想要Experiments
,每个都有它的ExperimentTypes
.
那么为什么不直接使用ICollection<...>
?
var experiments = myDbContext.Experiments
.Where(experiment => ...) // only if you don't want all experiments
.Select(experiment => new
{ // Select only the properties you actually plan to use
Id = experiment.Id,
Name = experiment.Name,
...
// get all or some of its ExperimentTypes
ExperimentTypes = experiment.ExperimentTypes
.Where(experimentType => ...) // only if you don't want all experiment types
.Select(experimentType => new
{
// again: select only the properties you plan to use
Id = experimentType.Id,
...
})
.ToList(),
});
实体框架知道多对多,它知道为此需要与联结表的三重连接,并将执行此三重连接。
在内部,这将是一个GroupJoin,你会得到Experiments
,每个都有他们的ExperimentTypes
. 您甚至可以获得还没有任何 ExperimentType 的 Experiments。
如果你真的想要inner join
,你必须用他们的 ExperimentTypes 来展平 Experiments。这是使用具有 resultSelector 作为参数的 SelectMany 的重载来完成的
// flatten the Experiments with their experimentTypes
var flatInnerJoin = myDbContext.Experiments.SelectMany(experiment => experiment.ExperimentTypes,
// from each experiment and one of its experimentTypes make one new object
(experiment, experimentType) => new
{
ExperimentId = experiment.Id,
ExperimentTypeId = experimentType.Id,
...
});
})
注意!这样您就不会得到没有 ExperimentTypes 的 Experiments,就像在标准内部连接中一样。
推荐阅读
- html - 警告是:页面的描述元标记中有非字母字符
- networking - 如果 ARP 没有找到关联的 IP 会发生什么
- python - 在 Python Tkinter 中保留输入字段
- typescript - 返回构造函数的工厂方法的类型
- python - 如何在python3中打印匹配字符串的下一行(如果我给 Tea ,它也应该打印下一行)
- php - Laravel 在更新之前使用数据库检查提交的值
- regex - 正则表达式匹配每个点
- react-native - 我可以在 PersistedGate 的加载屏幕上登录吗?Redux-Presist
- algorithm - 为什么具有可接受的非一致启发式的 A* 会找到非最优解?
- php - WordPress:删除管理页面,但仍然可以直接访问。