Hi guys I have a question about EF Fluent API (Code First :P). In my model I have
public class TABLE_A
{
public virtual long Id {get; set;}
....
public virtual TABLE_B MyTableBRef {get; set;}
}
public class TABLE_B
{
public virtual long Id {get; set;}
....
public virtual TABLE_A MyTableARef {get; set;}
}
How am I supposed to map a 0..1 to 0..1 relationship?
In the database on TABLE_B I have a column (FK) that references the PK of TABLE_A.
It is not possible to have navigational properties on both sides if you are using a column other than the PK of TABLE_B.
public class TABLE_A
{
public virtual long Id {get; set;}
}
public class TABLE_B
{
public virtual long Id {get; set;}
....
public virutal TABLE_A MyTableARef {get; set;}
}
public class MyContext : DbContext
{
public DbSet<TABLE_A> As { get; set; }
public DbSet<TABLE_B> Bs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TABLE_B>()
.HasRequired(b => b.MyTableARef)
.WithMany()
.Map(b => b.MapKey("FK_Column_name));
}
}
If the PK of TABLE_B is also a FK to TABLE_A then you can use shared PK mapping with navigational properties on both sides.
Edit:
You can use Shared PK mapping as follows
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TABLE_B>()
.HasRequired(b => b.MyTableARef)
.WithOptional(a => a.MyTableBRef);
}
The Id of TABLE_B is also an FK to TABLE_A.
Related
I'd like to add a table to define permission object than can be applied to many models.
To do this, I create a Permission class:
public class Permission
{
[Key]
int PermissionID {get; set;}
string User {get; set;}
bool Read {get; set;}
bool Write {get; set;}
}
And then other object classes than can have a List of Permission:
public class ObjectModel1
{
[Key]
int idObject1 {get; set;}
... Other properties ...
public virtual ICollection<Permission> Permission {get; set;}
}
public class ObjectModel2
{
[Key]
int idObject2 {get; set;}
... Other properties ...
public virtual ICollection<Permission> Permission {get; set;}
}
How can I obtaint a multiple many to many relationship between Permission and other Object classes without defining Foreign Keys in Permission class for each Object?
A many to many relationship will have an xref table between the two entities:
// because Permission has a collection to ObjectModel1 and ObjectModel1 has a collection
// to permission, it is treated as a many to many relationship with an implicit
// xref between the tables. The xref will contain a foreign key to each entity that is
// also a composite primary key
public class Permission
{
[Key]
int PermissionID {get; set;}
string User {get; set;}
bool Read {get; set;}
bool Write {get; set;}
public virtual ICollection<ObjectModel1> ObjectModel1s { get; set; }
public virtual ICollection<ObjectModel2> ObjectModel2s { get; set; }
}
public class ObjectModel1
{
[Key]
int idObject1 {get; set;}
... Other properties ...
public virtual ICollection<Permission> Permission {get; set;}
}
public class ObjectModel2
{
[Key]
int idObject2 {get; set;}
... Other properties ...
public virtual ICollection<Permission> Permission {get; set;}
}
Entity Framework will create a table that is something like PermissionObjectModel1 that has a composite primary key with two foreign keys (one to Permission, one to ObjectModel1). It will create another table for ObjectModel2 with similar keys. The foreign key doesn't exist on Permission itself.
If you don't want to have the navigation property on permission, then I think you will need to use the Fluent API:
public class MyDbContext : DbContext
{
public DbSet<Permission> Permissions { get; set; }
public DbSet<ObjectModel1> ObjectModel1s { get; set; }
public DbSet<ObjectModel2> ObjectModel2s { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ObjectModel1>()
.HasMany(many => many.Permissions)
.WithMany() // dont want navigation property on Permission
.Map(xref => xref.MapLeftKey("ObjectModel1Id")
.MapRightKey("PermissionId")
.ToTable("ObjectModel1PermissionXref"));
modelBuilder.Entity<ObjectModel2>()
.HasMany(many => many.Permissions)
.WithMany() // dont want navigation property on Permission
.Map(xref => xref.MapLeftKey("ObjectModel2Id")
.MapRightKey("PermissionId")
.ToTable("ObjectModel2PermissionXref"));
}
}
Something similar to the above code would still give you a many to many relationship, but the navigation property would not be defined on Permission.
How o define one to many relationship in EF with different primary and foreign key name
UPDATED
Public class Tb1
{
[Key]
public int ID{get; set;} // primary
**public int foreignKey{get; set;} //foreign key**
public string name{get; set;}
[Foreign("foreignKey")]
public virtual ICollection<Tb2> Tb2{ get; set; }
}
Public class Tb2
{
[Key]
public int ID {get; set;} //primary
public int tb1ID {get; set}
public string address {get; set;}
}
Here i want one to many relationsip on Primary key: foreignKey at TB1
foreign kye: tb1ID at TB2
HOW??
My Nomal approach would be to include the navigation property as well. So I would change Tb2 like this:
Public class Tb2
{
[Key]
public int ID {get; set;} //primary
public int Tb1ID {get; set;} //notice I changed case on this variable as well
public Tb1 Tb1 {get; set;} //this is the new variable
public string address {get; set;}
}
Code first should be able to automatically understand the relationship now. If you don't want the Tb1ID property, you can remove it, and it will still work out just fine.
I have 4 tables that like thus:
public class Table1
{
public int Id {get;set;}
public string Name {get;set;}
}
public class Table2
{
public int Id {get;set;}
public string Name {get;set;}
public int Table1Id {get;set;}
public virtual Table1 {get; set;}
}
public class Table3
{
public int Id {get;set;}
public string Name {get;set;}
public int Table2Id {get;set;}
public virtual Table2 {get; set;}
}
public class Table4
{
public int Id {get;set;}
public string Name {get;set;}
public int Table3Id {get;set;}
public virtual Table3 {get; set;}
}
And my fluent API is like thus:
modelBuilder.Entity<Table2>().HasRequired(x => x.Table1).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Table3>().HasRequired(x => x.Table2).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Table4>().HasRequired(x => x.Table3).WithMany().WillCascadeOnDelete(false);
When I try to seed the tables, I get this error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Table4_Table3_Table3Id.
The conflict occurred in database MyDb, table "dbo.Table3", column 'Id'.The statement has been terminated."
I cannot see where I am going wrong
do this...
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithMany() // t => t.AllTable2s)
.HasForeignKey(t => t.Table1ID);
...and above all make it compile ! :) (e.g. public virtual Table1 {get; set;} into public virtual Table1 Table1 {get; set;}
I have 3 tables that need to be mapped with Entity Framework and I'm not sure the proper way to go about this. Here are my 3 entities:
public class User
{
[Key]
public int user_id {get; set;}
public string user_name {get; set;}
public virtual List<Role> roles {get; set;}
}
public class Role
{
[Key]
public int role_id {get; set;}
public string role_name {get; set;}
}
public class User_Role
{
[Key]
public int user_role_id {get; set;}
public int user_id {get; set;}
public int role_id {get; set;}
}
Please note that the User_Role entity just represents a lookup table to link many roles to a single user.
With SQL I would just do something like:
SELECT User.user_name, Role.role_name FROM User INNER JOIN User_Role ON User_Role.user_id = User.user_id INNER JOIN Role ON Role.role_id = User_Role.role_id WHERE User.user_id = 123
I am relatively new to Entity Framework so I'm not sure the best way to tackle this using EF4 DbContext (and possibly Fluent API?) but I'm hoping its pretty straight forward.
Thanks in advance.
It turns out I needed to use Fluent API to map a many to many table (User_Role).
modelBuilder.Entity<Role>()
.HasMany<User>(u => u.users)
.WithMany(r => r.roles)
.Map(m =>
m.MapLeftKey("role_id")
m.MapRightKey("user_id")
m.ToTable("User_Role"));
I'm working to solve a very tedious problem.
I have a class called Nation and a class called NationAlly
public class Nation
{
public int ID {get; set;}
public int name {get;set;}
public List<NationAlly> NationAllies {get;set;}
}
public class NationAlly
{
public int ID {get; set;}
public int level {get;set;}
public Nation toNation {get;set;}
}
I'm using EF 4 and CodeFirst with a DbContext called NationsDB to manage my database on SQL Server 2008.
If I create a new object of type Nation and I try to call nationsDB.SaveChanges, I got the following exception:
"Multiplicity constraint violated. The role 'NationAlly_toNation_Target' of the relationship 'CodeFirstNamespace.NationAlly_toNation' has multiplicity 1 or 0..1."
I tried to save a Nation with NationAllies field null and this exception is not thrown, the nation table in the database gets all the correct values.
In my database the table Nation has 2 fields: ID(primary key), name
The table NationAlly has 3 fields: ID(primary key), level, NationID
The two tables are linked with a relationship where NationAlly.NationID is foreign key and Nation.ID is primary key.
Isn't strange? In my eyes the table NationAlly should have a field called NationID1 and another called NationID2 to create the "relationship" between a nation and a list of other nations.
What did I do wrong?
You are perhaps a victim of the EF Code-First mapping conventions which create automatically a relationship between NationAllies and toNation you don't want to have.
If I understand you correctly (but I am not 100 percent sure, if I do), you actually want to have two relationships and you have exposed only one end of the relationship in each of the entities. So, NationAllies does NOT point to toNation but to an "invisible" Owner nation in your NationAlly entity.
If that is the case you need to explicitly overwrite the convention mappings. In the Fluent API of EF 4.1 this could look like:
public class MyContext : DbContext
{
public DbSet<Nation> Nations { get; set; }
public DbSet<NationAlly> NationAllies { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Nation>()
.HasMany(n => n.NationAllies)
.WithRequired()
.Map(conf => conf.MapKey("OwnerID"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<NationAlly>()
.HasRequired(a => a.toNation)
.WithMany()
.Map(conf => conf.MapKey("NationID"))
.WillCascadeOnDelete(false);
}
}
This mapping would create the two foreign keys OwnerID and NationID in the NationAllies table, both pointing to the primary key ID in the Nations table.
Edit
Here is the application I have tested with:
Create a new Console App in VS2010 / .NET 4.0, name it "NationsApp"
Add a reference to "EntityFramework.dll"
Clear the content of "Program.cs" and paste instead the following in:
Content of Program.cs:
using System;
using System.Collections.Generic;
using System.Data.Entity;
namespace NationsApp
{
public class Nation
{
public int ID { get; set; }
public int name { get; set; }
public List<NationAlly> NationAllies { get; set; }
}
public class NationAlly
{
public int ID { get; set; }
public int level { get; set; }
public Nation toNation { get; set; }
}
public class NationsContext : DbContext
{
public DbSet<Nation> Nations { get; set; }
public DbSet<NationAlly> NationAllies { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Nation>()
.HasMany(n => n.NationAllies)
.WithRequired()
.Map(conf => conf.MapKey("OwnerID"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<NationAlly>()
.HasRequired(a => a.toNation)
.WithMany()
.Map(conf => conf.MapKey("NationID"))
.WillCascadeOnDelete(false);
}
}
class Program
{
static void Main(string[] args)
{
using (var context = new NationsContext())
{
try
{
// We have three Nations and two Allies
Nation nation1 = new Nation() {
NationAllies = new List<NationAlly>() };
Nation nation2 = new Nation() {
NationAllies = new List<NationAlly>() };
Nation nation3 = new Nation() {
NationAllies = new List<NationAlly>() };
NationAlly ally1 = new NationAlly();
NationAlly ally2 = new NationAlly();
// Nation1 has two Allies
// (Nation1 is the "owner" of both Allies)
nation1.NationAllies.Add(ally1);
nation1.NationAllies.Add(ally2);
// toNation of ally1 refers to Nation2
ally1.toNation = nation2;
// toNation of ally2 refers to Nation3
ally2.toNation = nation3;
context.Nations.Add(nation1);
context.Nations.Add(nation2);
context.Nations.Add(nation3);
context.SaveChanges();
}
catch (Exception e)
{
throw;
}
}
}
}
}
You can set a breakpoint on "throw" to watch possible exceptions in e in the debugger.
This creates a database called NationsApp.NationsContext if you are using SQL Server Express and don't have any further connection strings defined.
It gives two relationships Nation_NationAllies (FK is "OwnerID") and NationAlly_toNation (FK is "NationID"). All columns are non-nullable. The result in the DB is the following:
In case this helps someone getting this error... I was getting this message while doing queries rather than saving to the database. My data design:
public class Base {
public int Id {get; set;}
}
public class Child {
[Key][ForeignKey("Base")] public int Id {get; set;}
public virtual Base Base {get; set;}
public Child() {
Base = new Base();
}
}
The problem was in the constructor. Turns out EF4.1 doesn't like when you initialize associations there! I removed that constructor and things started working again.