简体   繁体   中英

LINQ to Entities: perform joins on many-to-many relationships (code first)

I have the following model:

[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>();
    }
}

The DbSet contains:

    public DbSet<Experiment> Experiments { get; set; }
    public DbSet<ExperimentType> ExperimentTypes{ get; set; }

And this creates a table on SQL, called ExperimentExperimentTypes.

Now, I would like to perform a LINQ join, like:

var query =
   from e in database.Experiments
   join eet in database.ExperimentExperimentTypes on eet.Experiment_Id equals eet.ExperimentType_Id ...

But obviously database.ExperimentExperimentTypes in not recognized in code.

I tried a lot of things in order to tell the code that there is this table, I also tried to create the corresponding c# class, but I'm not getting any result.

How can achieve that?

So you have two tables: Experiment and ExperimentType . There is a many-to-many relation between those two: every Experiment is an experiment of zero or more ExperimentTypes ; every ExperimentType is the type of zero or more Experiments .

This many-to-many can be seen in your class definitions. The virtual ICollection<...> on both sides indicates the many-to-many relationship.

In relational databases this many-to-many relation is implemented using a junction table. However, in entity framework you seldom see the junction table. It is in your database, however, you can't access it using your DbContext

But how am I going to perform a join between Experiments and ExperimentTypes if I can't access the junction table?

Well, Pooh bear should go back to his thinking spot. You don't want to join tables, you want Experiments , each with its ExperimentTypes .

So why not just do the query using the 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(),
    });

Entity framework knows the many-to-many, it knows that for this a triple join with the junction table is needed, and will perform this triple join.

Internally this will be a GroupJoin , you'll get Experiments , each with their ExperimentTypes . You even get Experiments that don't have any ExperimentType yet.

If you really want the inner join , you'll have to flatten the Experiments with their ExperimentTypes. This is done using the 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,
        ...
     });
 })

Nota bene! This way you won't get the Experiments that have no ExperimentTypes, just as in a standard inner join.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM