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);
}
Related
I have created a simple EF Core to join two tables by using relationship (HasOne). But when I run it, the query only queries the master table (Employees) without joining to the second table (Contact) and it causes the model to not bind the data.
Could someone point out what I am missing in this code shown below? Thanks
public class Employees
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string ContactNumber { get; set; }
public Employees Employee { get; set; }
public int EmployeeId { get; set; }
}
internal class EmployeeMap : IEntityTypeConfiguration<Employees>
{
public void Configure(EntityTypeBuilder<Employees> builder)
{
builder.HasKey(x => x.EmployeeId);
builder.Property(p => p.EmployeeId).ValueGeneratedOnAdd();
builder.HasOne(x => x.Contact).WithOne(y => y.Employee).HasForeignKey<Contact>(k => k.EmployeeId);
}
}
public class ContactMap : IEntityTypeConfiguration<Contact>
{
public void Configure(EntityTypeBuilder<Contact> builder)
{
builder.HasKey(x => x.Id);
builder.Property(p => p.Id).ValueGeneratedOnAdd();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
private EmployeeResponse GetEmployeeResponse()
{
var emp = _context.Employees.FirstOrDefault();
return new EmployeeResponse
{
ContactNumber = emp!.Contact.ContactNumber,
EmployeeId = emp.EmployeeId,
};
}
Solutions:
1. Enable lazy loading:
DbContext.Configuration.LazyLoadingEnabled = true;
2. Or load it manually with .Include:
_context.Employees.Include(x => x.Contact).FirstOrDefault();
More information about navigation propertys in ef.
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; }
}
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 solved this by adding in the ForeignKey("Route") data annotation like this:
[ForeignKey("Route")]
public Guid rnh_rtefk { get; set; }
this is my first attempt at using EF.
I have created a model using Code First from database in my MVC app using EF 6.0.0.0
In my database I have a table of Routes and a table of RunSheetHeader. A route can have many RunSheetHeader and a RunSheetHeader can have one Route. The Routes primary key is Routes.rte_pk and this maps to the foreign key: RunSheetHeader.rnh_rtefk.
The code generated is this:
public partial class Route
{
public Route()
{
RunSheetHeaders = new HashSet<RunSheetHeader>();
}
[Key]
public Guid rte_pk { get; set; }
[Required]
[StringLength(50)]
public string rte_name { get; set; }
public virtual ICollection<RunSheetHeader> RunSheetHeaders { get; set; }
}
[Table("RunSheetHeader")]
public partial class RunSheetHeader
{
public RunSheetHeader()
{
RunSheetDetails = new HashSet<RunSheetDetail>();
}
[Key]
public Guid rnh_pk { get; set; }
[Column(TypeName = "date")]
public DateTime rnh_date { get; set; }
public Guid rnh_rtefk { get; set; }
public virtual Route Route { get; set; }
public virtual ICollection<RunSheetDetail> RunSheetDetails { get; set; }
}
This is from the Context class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Route>()
.HasMany(e => e.RunSheetHeaders)
.WithRequired(e => e.Route)
.HasForeignKey(e => e.rnh_rtefk)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RunSheetHeader>()
.HasMany(e => e.RunSheetDetails)
.WithRequired(e => e.RunSheetHeader)
.HasForeignKey(e => e.rnd_rnhfk)
.WillCascadeOnDelete(false);
}
The error I get is:
"Invalid column name 'Route_rte_pk'."
and the SQL shows up in SQL Profiler as:
SELECT
1 AS [C1],
[Extent1].[rnh_pk] AS [rnh_pk],
[Extent1].[rnh_date] AS [rnh_date],
[Extent1].[rnh_rtefk] AS [rnh_rtefk],
[Extent1].[Route_rte_pk] AS [Route_rte_pk]
FROM [dbo].[RunSheetHeader] AS [Extent1]
From reading the other answers here regarding similar problems it seems to be a problem with mapping the correct foreign keys, but it looks to me like that has been done correctly. Can anyone spot what I am missing?
Thanks for any help
I found 2 possible solutions to this:
1.
[ForeignKey("Route")]
public Guid rnh_rtefk { get; set; }
or 2.
[ForeignKey("rnh_rtefk")]
public virtual Route Route { get; set; }
Both get rid of the error, but I was advised that the second option is the better one to use.
the following code is running just fine, are you sure your OnModelCreating is well called ?
namespace testef {
public partial class Route {
public Route() {
RunSheetHeaders = new HashSet<RunSheetHeader>();
}
[Key]
public Guid rte_pk { get; set; }
[Required]
[StringLength(50)]
public string rte_name { get; set; }
public virtual ICollection<RunSheetHeader> RunSheetHeaders { get; set; }
}
[Table("RunSheetHeader")]
public partial class RunSheetHeader {
public RunSheetHeader() {
//RunSheetDetails = new HashSet<RunSheetDetail>();
}
[Key]
public Guid rnh_pk { get; set; }
[Column(TypeName = "date")]
public DateTime rnh_date { get; set; }
public Guid rnh_rtefk { get; set; }
public virtual Route Route { get; set; }
//public virtual ICollection<RunSheetDetail> RunSheetDetails { get; set; }
}
// ---------------
public class TestEFContext : DbContext {
public TestEFContext(String cs)
: base(cs) {
Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Route>()
.HasMany(e => e.RunSheetHeaders)
.WithRequired(e => e.Route)
.HasForeignKey(e => e.rnh_rtefk)
.WillCascadeOnDelete(false);
//modelBuilder.Entity<RunSheetHeader>()
// .HasMany(e => e.RunSheetDetails)
// .WithRequired(e => e.RunSheetHeader)
// .HasForeignKey(e => e.rnd_rnhfk)
// .WillCascadeOnDelete(false);
}
public DbSet<Route> Routes { get; set; }
}
class Program {
String cs = #"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
using (TestEFContext ctx = new TestEFContext(cs)) {
Route r = new Route {
rte_pk = Guid.NewGuid(),
rte_name = "test"
};
r.RunSheetHeaders.Add(new RunSheetHeader {
rnh_pk = Guid.NewGuid(),
rnh_date = DateTime.Now
});
ctx.Routes.Add(r);
ctx.SaveChanges();
Console.WriteLine(ctx.Routes.Count());
}
using (TestEFContext ctx = new TestEFContext(cs)) {
foreach (Route r in ctx.Routes) {
Console.WriteLine(r.rte_name);
foreach (RunSheetHeader rsh in r.RunSheetHeaders) {
Console.WriteLine(" {0}", rsh.rnh_date);
}
}
}
}
}
}
Is it possible to specify the table name in a many-to-many relationship?
Example:
class A
{
public int Id { get; set; }
// .....
public virtual ICollection<B> BCollection { get; set; }
}
class B
{
public int Id { get; set; }
// .....
public virtual ICollection<A> ACollection { get; set; }
}
I thought that this could maybe work but apparently it doesn't.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>().HasMany(x => x.BCollection).WithMany(y => y.ACollection).Map(m => m.ToTable("My_Custom_Name"));
}
Try maapping the column names also.
modelBuilder.Entity<A>()
.HasMany(x => x.BCollection).WithMany(y => y.ACollection)
.Map(m =>
{
m.ToTable("My_Custom_Name");
m.MapLeftKey("AId");
m.MapRightKey("BId");
});