【问题标题】:LINQ to Entities: perform joins on many-to-many relationships (code first)LINQ to Entities:对多对多关系执行连接(代码优先)
【发布时间】:2018-11-12 17:03:50
【问题描述】:

我有以下型号:

[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#类,但没有得到任何结果。

如何做到这一点?

【问题讨论】:

  • 只需使用导航属性,它就会创建所有需要的连接。
  • "但显然 database.ExperimentExperimentTypes 在代码中无法识别。";如果表在那里,您可以在代码中定义它。这样就可以访问了。根据您的查询并正确执行,您可以通过这种方式提高性能。
  • (除了我之前的评论):或者按照@juharr 说的做 ;-)

标签: c# linq ef-code-first linq-to-entities code-first


【解决方案1】:

所以你有两个表:ExperimentExperimentType。这两者之间存在多对多关系:每个Experiment 是零个或多个ExperimentTypes 的实验;每个ExperimentType 是零个或多个Experiments 的类型。

这种多对多可以在您的类定义中看到。两边的virtual ICollection&lt;...&gt;表示多对多关系。

在关系数据库中,这种多对多关系是使用联结表实现的。但是,在实体框架中,您很少看到联结表。它在您的数据库中,但是您无法使用 DbContext 访问它

但是我将如何在 Experiments 和 如果我无法访问联结表,需要 ExperimentTypes?

嗯,小熊维尼应该回到他的思考点。你不想加入表,你想要Experiments,每个都有它的ExperimentTypes

那么为什么不直接使用ICollection&lt;...&gt; 进行查询呢?

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 的 Experiment。

如果你真的想要inner join,你必须用它们的 ExperimentTypes 来扁平化 Experiments。这是使用overload of SelectMany that has a resultSelector as parameter

// 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,就像在标准内部连接中一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-12
    • 2012-01-15
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    相关资源
    最近更新 更多