Search code examples
c#entity-frameworkef-code-firstfluent

Entity Framework Include Record with certain value in a Navigation Property


I am using Entity Framework 6 Code First and I'm configuring the mapping of my domain model with Fluent API. I don't see how to create a navigation properties for a Table which is a little tricky. I have several objects which can make noise, I would like to record that noise in a NoiseRecord Table.

I need some kind of conditional mapping, something like that :

modelBuilder.Entity<NoiseRecord>().HasRequired(n=>n.Origine.OrigineType()=="Car").WithMany(c=>c.NoiseRecords);

That would be the mapping of the Car Navigation Property to avoid that, for example, it includes record related to Planes.

Here is my code

public interface INoisy
{
    int ID {get; set;}
    string OriginType()
    ...
    //And other useful things not related to persistence
}


public class Car : INoisy
{
    ...
    ICollection<NoiseRecord> NoiseRecords { get; set; }
    string OrigineType()
    {
        return "Car";
    }
}

public class Plane : INoisy
{
    ...
    ICollection<NoiseRecord> NoiseRecords {get; set;}
    string OrigineType()
    {
        return "Plane";
    }
}

And a couple of other classes implement INoisy also. Below is the NoiseRecord Table.

public class NoiseRecord
{
    public int RecordID {get; set;}
    public INoisy NoiseOrigine {get; set;}
    public double NoiseMagnitude {get; set;}
}

I'm looking for a way to achieve that with Fluent API.

Thank you !


Solution

  • First of all, it is not possible to use interfaces as navigation properties. But you could use an abstract base class for your noise origins

    public abstract class NoiseOrigin
    {
        public NoiseOrigin()
        {
            this.NoiseRecords = new Collection<NoiseRecord>();
        }
    
        public int Id { get; set; }
    
        public ICollection<NoiseRecord> NoiseRecords { get; set; }
    }
    
    public class Car : NoiseOrigin {}
    
    public class Plane : NoiseOrigin { }
    
    public class NoiseRecord
    {
        public int Id { get; set; }
    
        public int OriginId { get; set; }
        public NoiseOrigin Origin { get; set; }
    
        public double NoiseMagnitude { get; set; }
    }
    

    Your fluent API mapping whould look like this

    public class NoiseModelContext : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<Car>().Map(p => p.Requires("Type").HasValue("Car"));
            modelBuilder.Entity<Plane>().Map(p => p.Requires("Type").HasValue("Plane"));
        }
    
        public DbSet<NoiseOrigin> NoiseOrigins { get; set; }
    
        public DbSet<NoiseRecord> NoiseRecords { get; set; }
    }
    

    To get all car noise records your query will look like

    using (var db = new NoiseModelContext()) {
        var records = db.NoiseRecords.Where(p => p.Origin is Car);
        // or like this - the result is the same.
        var records2 = db.NoiseOrigins.OfType<Car>().SelectMany(p => p.NoiseRecords);
    }