0

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 !

tobiak777
  • 3,175
  • 1
  • 32
  • 44

1 Answers1

0

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);
}
codeworx
  • 2,715
  • 13
  • 12
  • Thank you very much for your help, I really appreciate that. The problem with your solution is that my goal is to have the navigation properties "preloaded" automatically. I would like to avoid the need of manually populating the NoiseRecords property of my Cars/Plane. So there is actually something I don't understand with your solution, what is the point of your mapping if then you have to get the records with a Datacontext request ? If I have to write an extra request, then I would just throw the abstract class away since I'm not winning anything with it. – tobiak777 Jun 22 '14 at 17:02
  • In Entity Framework the fluent API can only be used to define how your Conceptual Model is mapped to your Database. There is no way to automatically preload navigation Properties. You can either use LazyLoading to load the navigation property on demand or use eager loading to include the values of your navigation property within your query. var result = db.NoiseOrigins.OfType().Include(p => p.NoiseRecords) – codeworx Jun 22 '14 at 20:07
  • With the abstract base class NoiseOrigin you allow your model to persist a Plane and a Car in the same database table by using TablePerHierarchy inheritance mapping. But more important, compared to an interface you can use a base class as a navigation property. The line modelBuilder.Entity().Map(p => p.Requires("Type").HasValue("Car")); simply means the class Car is mapped to the same Table as the base class NoiseOrigin but requires the column Type to hold the string value “Car” in order to be identified as an object of type Car. – codeworx Jun 22 '14 at 20:08