13

Is it possible to create one-way many-to-many association in entity framework 6 with code first and annotations? Example:

class Currency
{
    public int id { get; set; }
}

class Country
{
    public int id { get; set; }

    // How i can annotate this property to say EF that it is many-to-many
    // and it should create mapping table?
    // I don't need navigation property to Country in Currency class!
    public virtual IList<Currency> currencies { get; set; }
}

On Java + JPA annotations i can implement what i need this way:

@OneToMany
@JoinTable(name = "MAPPING_TABLE", joinColumns = {
    @JoinColumn(name = "THIS_ID", referencedColumnName = "ID")
}, inverseJoinColumns = {
    @JoinColumn(name = "OTHER_ID", referencedColumnName = "ID")
})

so, does EF have equal features?

Anton
  • 575
  • 2
  • 7
  • 27

3 Answers3

30

You can do this by specifying the relationship explicitly using the Fluent API. Override the OnModelCreating() method of your DbContext class, and in your override specify the details of the mapping table like this:

class MyContext : DbContext
{
    public DbSet<Currency> Currencies { get; set; }
    public DbSet<Country> Countries { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Country>()
            .HasMany(c => c.Currencies)
            .WithMany()                 // Note the empty WithMany()
            .Map(x =>
            {
                x.MapLeftKey("CountryId");
                x.MapRightKey("CurrencyId");
                x.ToTable("CountryCurrencyMapping");
            });

        base.OnModelCreating(modelBuilder);
    }
}

Note that - in my quick test anyway - you will have to Include() the Currencies property when loading the EF object to have the list populated:

            var us = db.Countries
                        .Where(x => x.Name == "United States")
                        .Include(x=>x.Currencies)
                        .First();

EDIT

If you really want to do everything with Data Annotations, and not use Fluent at all, then you can model the join table explicitly as pointed out elsewhere. There are various usability disadvantages of this approach, though, so it seems the Fluent method is the best approach.

class Country
{
    public int Id { get; set; }
    public virtual ICollection<CountryCurrency> CountryCurrencies { get; set; }
}

class Currency
{
    public int Id { get; set; }
}

class CountryCurrency
{
    [Key, Column(Order=0)]
    public virtual int CountryId { get; set; }
    [Key, Column(Order=1)]
    public virtual int CurrencyId { get; set; }

    public virtual Country Country { get; set; }
    public virtual Currency Currency { get; set; }
}
Community
  • 1
  • 1
Myk Willis
  • 12,306
  • 4
  • 45
  • 62
  • Thank you... Is it possible to do this configuration only with annotations? – Anton Jun 07 '13 at 11:42
  • I think the best you could do would be to model the join table explicitly. So, make a CountryCurrency class alongside your other EF objects, and replace the `currencies` property in the Country object with an `ICollection`. This has usability disadvantages, though, because EF won't be automatically doing the JOINS behind the scenes for you. – Myk Willis Jun 07 '13 at 13:48
  • Strange that there is no way to do this with attributes. EF is a mess. – yonexbat Aug 10 '13 at 08:10
  • And what about cascade deleting from CountryCurrencyMapping? Does EF automatically clean up unused navigation records? – oxfn Oct 01 '13 at 06:13
0

I think you wanna learn how to separate relations from EF code first entities. I have started a topic in here about this question. I wanna separate the relation objects from entity, and I used partial classes. In my question I wanna learn how to separate partial classes by class bilrary. But couldn't.

While I was using NHibernate I was using XML mapping and creating relations here, In java platform is same thing. But I think Entity Framework is not ready yet.

Community
  • 1
  • 1
bayramucuncu
  • 1,014
  • 10
  • 20
0

You can do this in code first quite easily in EF 6.

    public class Country
{
   public int ID {get;set;}

   public virtual ICollection<Currency> Currencys {get;set;}//don't worry about the name,     pluralisation etc


}

public class Currency
{

   public int ID {get;set;}

   public virtual ICollection<Country> Countrys {get;set;}//same as above - 

}

Compile it, run it and hey presto - magic join table in the background. Depends if the naming conventions bother you. I personally think if you are doing code first, you should do it all in the code. Some people prefer annotation, some prefer fluent API - use whichever you prefer.

Will
  • 367
  • 3
  • 4
  • The question was explicit about a one way navigation. Adding Countries to the Currency removes that restriction. – KyorCode Apr 24 '15 at 14:30
  • I hear what you're saying, but from what I can tell 1 way navigation properties kind of go against code first principles. What is the disadvantage of putting both collections in? Does it become less performant? – Will Jun 08 '15 at 09:51