Today I got a question about how to create a many to many mapping using Entity Framework Code First fluent api.
The problem is that the entity create an additional table beyond that was set for me.
public class Person
{
public Person()
{
courses = new HashSet<Course>();
}
public int PersonID { get; set; }
public String Name { get; set; }
public ICollection<Course> courses { get; set; }
}
public class Course
{
public Course()
{
people = new HashSet<Person>();
}
public int CourseID { get; set; }
public String Name { get; set; }
public ICollection<Person> people { get; set; }
}
public class PersonCourse
{
public int fk_CourseID { get; set; }
public virtual Course course { get; set; }
public int fk_PersonID { get; set; }
public virtual Person person { get; set; }
public String AnotherInformation { get; set; }
}
public class PersonDataConfiguration : EntityTypeConfiguration<Person>
{
public PersonDataConfiguration()
{
ToTable("Person");
Property(c => c.Name).IsRequired();
this.HasMany(c => c.courses).WithMany(t => t.people).Map(m => { m.MapLeftKey("CourseID"); m.MapRightKey("PersonID"); });
}
}
public class CourseDataConfiguration : EntityTypeConfiguration<Course>
{
public CourseDataConfiguration()
{
ToTable("Course");
Property(c => c.Name).IsRequired();
this.HasMany(c => c.people).WithMany(t => t.courses).Map(m => { m.MapLeftKey("PersonID"); m.MapRightKey("CourseID"); });
}
}
public class PersonCourseDataConfiguration : EntityTypeConfiguration<PersonCourse>
{
public PersonCourseDataConfiguration()
{
ToTable("PersonCourseX");
HasKey(c => new { c.fk_CourseID, c.fk_PersonID });
Property(c => c.AnotherInformation).IsRequired();
this.HasRequired(c => c.person).WithMany().HasForeignKey(t => t.fk_PersonID);
this.HasRequired(c => c.course).WithMany().HasForeignKey(t => t.fk_CourseID);
}
}
public class ProgramTesteContext : DbContext
{
public ProgramTesteContext()
: base("MyConnectionString")
{
}
public DbSet<Person> Person { get; set; }
public DbSet<Course> Course { get; set; }
public DbSet<PersonCourse> PersonCourse { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Properties<String>()
.Configure(p => p.HasColumnType("varchar"));
modelBuilder.Properties<String>()
.Configure(p => p.HasMaxLength(100));
modelBuilder.Configurations.Add(new PersonDataConfiguration());
modelBuilder.Configurations.Add(new CourseDataConfiguration());
modelBuilder.Configurations.Add(new PersonCourseDataConfiguration());
}
}
The entity set up two tables for mapping:
PersonCourseX created by me and another CoursePerson table containing only foreign keys without anotherinformation field.
How to make this second table is not created?
Change PersonCourseDataConfiguration as follows:
public class PersonCourseDataConfiguration : EntityTypeConfiguration<PersonCourse>
{
public PersonCourseDataConfiguration()
{
ToTable("PersonCourseX");
HasKey(c => new { c.fk_CourseID, c.fk_PersonID });
Property(c => c.AnotherInformation).IsRequired();
this.HasRequired(c => c.person).WithMany(c => c.courses).HasForeignKey(t => t.fk_PersonID);
this.HasRequired(c => c.course).WithMany(c => c.people).HasForeignKey(t => t.fk_CourseID);
}
}
Remove the commented lines:
public class PersonDataConfiguration : EntityTypeConfiguration<Person>
{
public PersonDataConfiguration()
{
ToTable("Person");
Property(c => c.Name).IsRequired();
//this.HasMany(c => c.courses).WithMany(t => t.people).Map(m => { m.MapLeftKey("CourseID"); m.MapRightKey("PersonID"); });
}
}
public class CourseDataConfiguration : EntityTypeConfiguration<Course>
{
public CourseDataConfiguration()
{
ToTable("Course");
Property(c => c.Name).IsRequired();
//this.HasMany(c => c.people).WithMany(t => t.courses).Map(m => { m.MapLeftKey("PersonID"); m.MapRightKey("CourseID"); });
}
}
Change Person and Course as follows:
public class Person
{
//.. other properties
public ICollection<PersonCourse> courses { get; set; }
}
public class Course
{
//.. other properties
public ICollection<PersonCourse> people { get; set; }
}
Related
How to configure EF mapping for the following relations:
public class OrderBook : BaseEntity
{
public OrderBook()
{
BuyOrders = new List<OrderBookItem>();
SellOrders = new List<OrderBookItem>();
}
public virtual ICollection<OrderBookItem> BuyOrders { get; set; }
public virtual ICollection<OrderBookItem> SellOrders { get; set; }
}
public class OrderBookItem : BaseEntity
{
public OrderType Type { get; set; }
public int OrderBookId { get; set; }
public virtual OrderBook OrderBook { get; set; }
}
I have tried a few variations
public class OrderBookMap : DynamicLoadEntityTypeConfiguration<OrderBook>
{
public OrderBookMap()
{
ToTable(nameof(OrderBook));
this.HasKey(p => p.Id);
//this does not work
//this.HasMany<OrderBookItem>(p=>p.BuyOrders).WithRequired(i=>i.OrderBook).HasForeignKey<int>(s => s.OrderBookId);
//this.HasMany<OrderBookItem>(p=>p.SellOrders).WithRequired(i=>i.OrderBook).HasForeignKey<int>(s => s.OrderBookId);
}
}
public class OrderBookItemMap : EntityTypeConfiguration<OrderBookItem>
{
public OrderBookItemMap()
{
ToTable(nameof(OrderBookItem));
this.HasKey(p => p.Id);
//this leads to extra columns created
this.HasRequired(i => i.OrderBook).WithMany(d => d.BuyOrders).HasForeignKey(i => i.OrderBookId).WillCascadeOnDelete(false);
this.HasRequired(i => i.OrderBook).WithMany(d => d.SellOrders).HasForeignKey(i => i.OrderBookId).WillCascadeOnDelete(false);
}
}
But the mapping result is incorrect, i think it is enough to have one FK between OrderBook and OrderBookItem tables:
the Type property might be used somehow as it helps to distinct which item is Buy or Sell.
In my case I decided it would be better to split it on 3 tables rather than having OrderBook_id1 and OrderBook_id2:
OrderBook
BuyOrder
SellOrder
So the entities and mapping end up as the following:
public class OrderBook : BaseEntity
{
public OrderBook()
{
BuyOrders = new List<BuyOrder>();
SellOrders = new List<SellOrder>();
}
public virtual ICollection<BuyOrder> BuyOrders { get; set; }
public virtual ICollection<SellOrder> SellOrders { get; set; }
}
public abstract class OrderBookItem : BaseEntity
{
public int OrderBookId { get; set; }
public virtual OrderBook OrderBook { get; set; }
}
public class BuyOrder : OrderBookItem
{
}
public class SellOrder : OrderBookItem
{
}
public class OrderBookMap : EntityTypeConfiguration<OrderBook>
{
public OrderBookMap()
{
ToTable(nameof(OrderBook));
this.HasKey(p => p.Id);
}
}
public class BuyOrderMap : EntityTypeConfiguration<BuyOrder>
{
public BuyOrderMap()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable(nameof(BuyOrder));
});
this.HasKey(p => p.Id);
this.HasRequired(i => i.OrderBook).WithMany(d => d.BuyOrders).HasForeignKey(i => i.OrderBookId).WillCascadeOnDelete(false);
}
}
public class SellOrderMap : EntityTypeConfiguration<SellOrder>
{
public SellOrderMap()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable(nameof(SellOrder));
});
this.HasKey(p => p.Id);
this.HasRequired(i => i.OrderBook).WithMany(d => d.SellOrders).HasForeignKey(i => i.OrderBookId).WillCascadeOnDelete(false);
}
}
I have seriously spent two work days trying to a TPH setup from Database First to Code first. The Error I get is Something like "Invalid Column Name Entity_EntityId/ Entity_Entity_Id1"
I've drawn up a very basic reproduction of the issue like so:
internal class Program
{
private static void Main(string[] args)
{
using (var context = new Context())
{
var baseClass = new Base {Name = "Test"};
context.BaseClasses.Add(baseClass);
context.SaveChanges();
var baseClasses = context.BaseClasses.ToList();
}
}
}
Context:
public class Context : DbContext
{
public Context() : base("TPH")
{
}
public DbSet<Base> BaseClasses { get; set; }
public DbSet<Derived> DervDerivedClasses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Mapping:
public class BaseMap : EntityTypeConfiguration<Base>
{
public BaseMap()
{
HasKey(b => b.Id);
Property(b => b.Name);
HasOptional(b => b.AnotherClass)
.WithMany(b => b.Bases)
.HasForeignKey(b => b.AnotherClassId);
Map(b => b.Requires("Disc").HasValue(1));
}
}
public class DerivedMap : EntityTypeConfiguration<Derived>
{
public DerivedMap()
{
HasKey(b => b.Id);
Property(b => b.Name);
HasOptional(b => b.AnotherClass)
.WithMany(b => b.Deriveds)
.HasForeignKey(b => b.AnotherClassId);
Map(b => b.Requires("Disc").HasValue(2));
}
}
public class SecondDerivedMap : EntityTypeConfiguration<SecondDerived>
{
public SecondDerivedMap()
{
HasKey(b => b.Id);
Property(b => b.Name);
HasOptional(b => b.AnotherClass)
.WithMany(b => b.SecondDeriveds)
.HasForeignKey(b => b.AnotherClassId);
Map(b => b.Requires("Disc").HasValue(3));
}
}
Entities:
public class Base
{
public int Id { get; set; }
public string Name { get; set; }
public int? AnotherClassId { get; set; }
public AnotherClass AnotherClass { get; set; }
}
public class Derived : Base
{
}
public class SecondDerived : Base
{
}
public class AnotherClass
{
public int Id { get; set; }
public ICollection<Base> Bases { get; set; }
public ICollection<Derived> Deriveds { get; set; }
public ICollection<SecondDerived> SecondDeriveds { get; set; }
}
How can I get the table to just have a single "AnotherClassId?"
You're only supposed to have a single navigation property per entity per relationship -- and you have three (Bases, Deriveds, and SecondDeriveds). EF sees those properties and thinks there are three different one-to-many associations between AnotherClass and the various classes in the Base hierarchy.
If you want to get a collection of the related Derived entities from AnotherClass, you're supposed to use something like anotherClassEntity.Bases.OfType<Derived>().
I have Many To Many relationship defined and when I try to query for the records that should be in the map I get null.
public class Record
{
public int RecordId { get; set; }
public DateTime DateRecordCreated { get; set; }
public ICollection<Street> Streets { get; set; }
public ICollection<Street> CrossStreets { get; set; }
}
public class RecordMap : EntityTypeConfiguration<Record>
{
public RecordMap()
{
// Primary Key
this.HasKey(t => t.RecordId);
this.HasMany(r => r.Streets)
.WithMany(c => c.Records)
.Map(sl =>
{
sl.ToTable("StreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.HasMany(r => r.CrossStreets)
.WithMany(c => c.AnotherRecord)
.Map(sl =>
{
sl.ToTable("AnotherStreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.Property(t => t.DateRecordCreated).IsRequired();
}
}
public class House : Record
{
public string HouseNumber { get; set; }
public string StreeName { get; set; }
public int ZipCode { get; set; }
}
public class Street
{
public int StreetId { get; set; }
public string StreetName { get; set; }
public ICollection<Record> Records { get; set; }
public ICollection<Record> AnotherRecord { get; set; }
}
Now when I run the following query below I get houses.CrossStreets as null, I tried adding enabling lazy loading and had the same out come.
public static void GetRecords()
{
using (var context = new SandboxContext())
{
var entities = context.Houses.Include(r => r.CrossStreets);
var houses = entities.ToList();
}
}
Firebird 2.5
Entity Framework 5
FirebirdClientDll 3.0.0.0
I'm (still) trying to access my legacy database with the Entity Framework (Code First).
Now I want to create a one to may relationship without a Forrein Key.
public class KONTAKTE
{
public int KUNDENNR { get; set; }
public Int16 ANSPRNR { get; set; }
public Int16 NR { get; set; }
public virtual ICollection<KONTAKTBED> KONTAKTBED { get; set; }
}
public class KONTAKTBED
{
public int ID { get; set; }
public Int16 LFDNR { get; set; }
public int KUNDENNR { get; set; }
public Int16 ANSPRNR { get; set; }
public string NAME { get; set; }
}
public class CTKontakt : DbContext
{
public DbSet<KONTAKTE> KONTAKTE { get; set; }
public DbSet<KONTAKTBED> KONTAKTBED { get; set; }
public CTKontakt(DbConnection connectionString) : base(connectionString, false)
{
Database.SetInitializer<CTKontakt>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
/* modelBuilder.Entity<KONTAKTE>().
HasMany(p => p.KONTAKTBED).
WithMany().
Map(t => t.MapLeftKey("KUNDENNR", "NR")
.MapRightKey("KUNDENNR", "LFDNR"));*/ //Does't work
modelBuilder.Entity<KONTAKTE>().HasKey(a => new { a.KUNDENNR, a.ANSPRNR, a.NR });
modelBuilder.Entity<KONTAKTBED>().HasKey(a => new { a.ID, a.DATABASE_ID});
base.OnModelCreating(modelBuilder);
}
As you can see I can't use the whole Primary Key of the KONTAKTE-Table. Does this mean I have to implemet a many to many realitonship? Currently I just join the tables later:
from k in lEKontakt.KONTAKTE
join kbed in lEKontakt.KONTAKTBED
on new { KUNDENNR = k.KUNDENNR, NR = k.NR }
equals new { KUNDENNR = kbed.KUNDENNR, NR = kbed.LFDNR }
I want to do something like this:
modelBuilder.Entity<KONTAKTE>()
.HasKey(d => new { d.KUNDENNR, d.ANSPRNR, d.NR })
.HasMany(d => d.KONTAKTBED)
.WithOptional()
.HasForeignKey(l => new { l.KUNDENNR, l.ANSPRNR, l.LFDNR });
But without the ANSPRNR...
I'm still new to Ef-Code First and all samples I find seem not to work under EF 5...
I found a solution for me:
It semms I only had to Remove the Primary Key definiton of the not used Field. I can still read the Data without it:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<KONTAKTE>()
.HasRequired(b => b.KUNDEN)
.WithMany()
.HasForeignKey(b => b.KUNDENNR);
modelBuilder.Entity<KONTAKTE>()
.HasKey(d => new { d.KUNDENNR, d.NR })
.HasMany(d => d.KONTAKTBED)
.WithOptional()
.HasForeignKey(l => new { l.KUNDENNR, l.LFDNR });
modelBuilder.Entity<KONTAKTE>().HasKey(a => new { a.KUNDENNR, a.NR });
modelBuilder.Entity<KUNDEN>().HasKey(a => new { a.KUNDENNR });
modelBuilder.Entity<KONTAKTBED>().HasKey(a => new { a.ID, a.DATABASE_ID});
base.OnModelCreating(modelBuilder);
}
I have two entites:
public class Address
{
public int Id { get; set; }
public string FirstName { get; set;
public string LastName { get; set; }
}
public partial class Customer
{
public int Id { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public virtual Address BillingAddress { get; set; }
public virtual Address ShippingAddress { get; set; }
}
Below are mapping classes:
public partial class AddressMap : EntityTypeConfiguration<Address>
{
public AddressMap()
{
this.ToTable("Addresses");
this.HasKey(a => a.Id);
}
}
public partial class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
this.ToTable("Customer");
this.HasKey(c => c.Id);
this.HasOptional<Address>(c => c.BillingAddress);
this.HasOptional<Address>(c => c.ShippingAddress);
}
}
When database is generated, my 'Customer' table has two columns for 'BillingAddress' and 'ShippingAddress' properties. Their names are 'AddressId' and 'AddressId1'.
Question: how can I rename them to 'BillingAddressId' and 'ShippingAddressId'?
Basically you want to customize the FK column name in an independent association and this code will do this for you:
public CustomerMap()
{
this.ToTable("Customer");
this.HasOptional<Address>(c => c.BillingAddress)
.WithMany()
.IsIndependent().Map(m =>
{
m.MapKey(a => a.Id, "BillingAddressId");
});
this.HasOptional<Address>(c => c.ShippingAddress)
.WithMany()
.IsIndependent().Map(m =>
{
m.MapKey(a => a.Id, "ShippingAddressId");
});
}