Convert EF Model Property to Navigation Property - entity-framework

What are the step to convert/migrate a model property into a navigation property (create a new class and create a foreign key relationship, using EF Code First Migration.
In the example below, I want to convert the Student class property Country into a navigational property, without losing in data.
Current Model
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Country { get; set; }
}
Proposed Model
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int CountryID { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public int ID { get; set; }
public string Country { get; set; }
}
Add-Migration NavigationProperty
public override void Up()
{
CreateTable(
"dbo.Countries",
c => new
{
ID = c.Int(nullable: false, identity: true),
CountryName = c.String(),
})
.PrimaryKey(t => t.ID);
AddColumn("dbo.Students", "CountryID", c => c.Int(nullable: false));
CreateIndex("dbo.Students", "CountryID");
AddForeignKey("dbo.Students", "CountryID", "dbo.Countries", "ID", cascadeDelete: true);
DropColumn("dbo.Students", "Country");
}
Update-Database Error
System.Data.SqlClient.SqlException (0x80131904): The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_dbo.Students_dbo.Countries_CountryID". The conflict occurred in database "aspnet-navprop-20141009041805", table "dbo.Countries", column 'ID'.

Related

Entity Framework 1:1 mapping foreign key

My entity AppUser has an optional UserProfile, and UserProfile as a required AppUser. I would like to have a foreign key to each other.
public class AppUser
{
public int Id { get; set; }
public string Name { get; set; }
public UserProfile UserProfile { get; set; }
public int? UserProfileId { get; set; }
}
public class UserProfile
{
public int Id { get; set; }
public string SomeUserProfileValue { get; set; }
public AppUser AppUser { get; set; }
public int AppUserId { get; set; }
}
I got this mapping:
modelBuilder.Entity<AppUser>().HasOptional(x => x.UserProfile).WithRequired(x => x.AppUser)
This generate the following migration. I notice there is no foreign key from AppUser to UserProfile. Also the foreignkey in UserProfile is defined on UserProfile.Id ... I want it on UserProfile.AppUserId.
public override void Up()
{
CreateTable(
"dbo.AppUsers",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
UserProfileId = c.Int(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.UserProfiles",
c => new
{
Id = c.Int(nullable: false),
SomeUserProfileValue = c.String(),
AppUserId = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AppUsers", t => t.Id)
.Index(t => t.Id);
}
So I tried to change the mapping configuration as follow
modelBuilder.Entity<AppUser>().HasOptional(x => x.UserProfile).WithRequired(x => x.AppUser)
.Map(c => c.MapKey("AppUserId"));
But now when I try to add the migration i get the error:
AppUserId: Name: Each property name in a type must be unique. Property name 'AppUserId' is already defined.
This seems to complain that I have a field AppUserId already defined in my model.
This is how we define our entities, we always include both the class and the id fields, gives more flexibility as to which to use under different circumstances.
So I'm a bit stuck here... is there any way to have this 1:1 bidirectional relation while having both class and the id fields defined in the model ?
And why there is no nullable foreign key generated in the AppUser table ?
I've generally found better results with DataAnnotations, myself. So:
public class AppUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int? UserProfileId { get; set; }
[ForeignKey = "UserProfileId"]
public UserProfile UserProfile { get; set; }
}
public class UserProfile
{
[Key]
public int Id { get; set; }
public string SomeUserProfileValue { get; set; }
public int AppUserId { get; set; }
[ForeignKey = "AppUserId"]
public AppUser AppUser { get; set; }
}

EF code first - add inverse propterty with optional element

I have the following classes
public class Order {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
[MaxLength(100)]
public string From { get; set; }
public int? TreatGuestEntryID { get; set; }
[ForeignKey("TreatGuestEntryID")]
public TreatedGuestEntry TreatGuestEntry { get; set; }
...
public class TreatedGuestEntry {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[MaxLength(200)]
public string Company { get; set; }
public string TypeOfTreat { get; set; }
This works as expected - in my Orders table it creates the foreign key.
Now I want to add an inverse property in TreatedGuestEntry for the order.
The best (at least somehow working) result I get when I add
modelBuilder.Entity<TreatedGuestEntry>()
.HasOptional(a => a.Order)
.WithOptionalDependent(a => a.TreatGuestEntry)
.Map(a=>a.MapKey("TreatGuestEntryID"));
and further rename the key of TreatedGuestEntry to TreatGuestEntryID.
But I get no relation in the database and also TreatGuestEntryID in the table Order is no longer a key (FK).
My approach in simple words:
In my Order I want an optional TreatedGuestEntry (and I need access to the foreign key) - and further in the related TreatedGuestEntry I want to access the Order.
In your case, the FK TreatGuestEntryID is not a PK, it means that it is a 1:n relationship. So, you have to put a Collection of Order on the other side:
public class Order
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
[MaxLength(100)]
public string From { get; set; }
public int? TreatGuestEntryID { get; set; }
public TreatedGuestEntry TreatGuestEntry { get; set; }
}
public class TreatedGuestEntry
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[MaxLength(200)]
public string Company { get; set; }
public string TypeOfTreat { get; set; }
public ICollection<Order> Orders { get; set; }
}
Mapping:
modelBuilder.Entity<Order>()
.HasOptional(i => i.TreatGuestEntry)
.WithMany(i => i.Orders)
.HasForeignKey(i => i.TreatGuestEntryID)
.WillCascadeOnDelete(false);
Generated Migration:
CreateTable(
"dbo.Orders",
c => new
{
ID = c.Int(nullable: false, identity: true),
Date = c.DateTime(nullable: false),
From = c.String(nullable: false, maxLength: 100),
TreatGuestEntryID = c.Int(),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.TreatedGuestEntries", t => t.TreatGuestEntryID)
.Index(t => t.TreatGuestEntryID);
CreateTable(
"dbo.TreatedGuestEntries",
c => new
{
ID = c.Int(nullable: false, identity: true),
Company = c.String(maxLength: 200),
TypeOfTreat = c.String(),
})
.PrimaryKey(t => t.ID);

EF Code First cascade delete doesn't work

I have 4 tables:
User table
public enum SEX { Male, Female }
public abstract class User
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public SEX Sex { get; set; }
}
Doctor table inherites from User
[Table("Doctor")]
public class Doctor : User
{
public string Department { get; set; }
public string Occupation { get; set; }
public string CabinetNumber { get; set; }
public virtual List<Treat> Treats { get; set; }
}
Patient table inherites from User
[Table("Patient")]
public class Patient : User
{
public int InsuranceNumber { get; set; }
public int CardNumber { get; set; }
public virtual List<Treat> Treats { get; set; }
}
public class Treat
{
public int TreatId { get; set; }
public int DoctorUserId { get; set; }
public int PatientUserId { get; set; }
public virtual Doctor Doctor { get; set; }
public virtual Patient Patient { get; set; }
}
public class HospitalContext: DbContext
{
public HospitalContext() : base("DBConnectionString") {
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<HospitalContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Treat>()
.HasRequired(x => x.Doctor)
.WithMany( x => x.Treats)
.HasForeignKey( x => x.DoctorUserId)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Treat>()
.HasRequired(x => x.Patient)
.WithMany( x => x.Treats)
.HasForeignKey( x => x.PatientUserId)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { get; set; }
public DbSet<Treat> Treats { get; set; }
}
I have found much answers here but no one from them works. I have spend a few hours trying to make it work. I know that Entity Framework must enable cascade delete when there is one-to-many relation, but it didn't
Entity Framework doesn't apply cascade deletion with TPT (Table Per Type) inheritance. You can solve this with Code Fist migrations:
CreateTable(
"dbo.Treats",
c => new
{
TreatId = c.Int(nullable: false, identity: true),
DoctorUserId = c.Int(nullable: false),
PatientUserId = c.Int(nullable: false),
})
.PrimaryKey(t => t.TreatId)
.ForeignKey("dbo.Doctor", t => t.DoctorUserId, cascadeDelete: true)
.ForeignKey("dbo.Patient", t => t.PatientUserId, cascadeDelete: true)
.Index(t => t.DoctorUserId)
.Index(t => t.PatientUserId);
The important part is cascadeDelete: true. You have to manually add it after migration code generation. After that you will have cascade deletion in your database:
FOREIGN KEY ([DoctorUserId]) REFERENCES [dbo].[Doctor] ([UserID]) ON DELETE CASCADE,
FOREIGN KEY ([PatientUserId]) REFERENCES [dbo].[Patient] ([UserID]) ON DELETE CASCADE

EF code first from database 0..1 to many relationship

I am trying to generated an entity framework code first model from an existing database (without changing the database schema). This database has been used in the past to generate edmx models and I am trying to achieve the equivalent model using Fluent Api or data annotations.
The relationship I have been unable to reproduce is 0..1 to many using a join table (not a nullable foreign key).
So it would look something like this:
TableA
{
ID (PrimaryKey)
TableB (0 or 1)
}
JoinTable
{
TableA_FK (PrimaryKey, ForeignKey),
TableB_FK (ForeignKey)
}
TableB
{
ID (PrimaryKey)
TableAs (Many)
}
Is this achievable in the code first style or will I have to generate an edmx model in order to use this database in EF without changing its schema?
Many thanks,
Phil
Here is an example without using a JoinTable class. The join table is configured through the fluent api.
class DataContext : DbContext
{
public DataContext(string connectionString)
: base(connectionString)
{ }
public DbSet<TableA> TableA { get; set; }
public DbSet<TableB> TableB { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<TableA>().ToTable("TableA");
modelBuilder.Entity<TableB>().ToTable("TableB");
modelBuilder.Entity<TableB>()
.HasMany(x => x.TableAs)
.WithMany()
.Map(m =>
{
m.ToTable("JoinTable");
m.MapLeftKey("TableA_FK");
m.MapRightKey("TableB_FK");
});
}
}
class TableA
{
public int ID { get; set; }
public TableB TableB { get; set; }
}
class TableB
{
public int ID { get; set; }
public ICollection<TableA> TableAs { get; set; }
}
This will generate the following migration script, which looks like the schema you have.
public override void Up()
{
CreateTable(
"dbo.TableA",
c => new
{
ID = c.Int(nullable: false, identity: true),
TableB_ID = c.Int(),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.TableB", t => t.TableB_ID)
.Index(t => t.TableB_ID);
CreateTable(
"dbo.TableB",
c => new
{
ID = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ID);
CreateTable(
"dbo.JoinTable",
c => new
{
TableA_FK = c.Int(nullable: false),
TableB_FK = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.TableA_FK, t.TableB_FK })
.ForeignKey("dbo.TableB", t => t.TableA_FK, cascadeDelete: true)
.ForeignKey("dbo.TableA", t => t.TableB_FK, cascadeDelete: true)
.Index(t => t.TableA_FK)
.Index(t => t.TableB_FK);
}
If I've understood correctly, the following code using only data annotations should create your model.
public class TableA
{
public int ID { get; set; }
public JoinTable JoinTable { get; set; }
}
public class TableB
{
public int ID { get; set; }
public List<JoinTable> JoinTables{ get; set; }
}
public class JoinTable
{
[Key, ForeignKey("TableA")]
public int TableA_FK { get; set; }
[ForeignKey("TableB")]
public int TableB_FK { get; set; }
public TableA TableA { get; set; }
public TableB TableB { get; set; }
}
Interestingly, EF does not perform a round trip back to the original, if you generate the code-first models from the database model that this code creates then EF simplifies the model and removes the join table and creates a nullable foreign key.
Let me know if this works.
I may be wrong, but I believe you're missing some concepts here...
Why you have a JoinTable if it's doesn't have any column besides its foreign keys? It doesn't make sense... IHMO a nullable foreign key in TableA would be correct way.
When you work with Code-First it means that everything in your database will be represented by CODE. There's no reason to have a table in your database but not in your code...
EDMX handles that relationship because it uses "Associations" https://msdn.microsoft.com/en-us/data/jj713299#Overview
...backing to the code-first, you can represent your database like this:
public class JoinTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int TableA_FK { get; set; }
public int TableB_FK { get; set; }
//a future property here
public virtual TableA TableA { get; set; }
public virtual TableB TableB { get; set; }
}
public partial class TableA
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TableAId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public virtual JoinTable JoinTable { get; set; }
}
public partial class TableB
{
public TableB()
{
JoinTable = new HashSet<JoinTable>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TableBId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public virtual ICollection<JoinTable> JoinTable { get; set; }
}
}
public partial class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
public virtual DbSet<JoinTable> JoinTable { get; set; }
public virtual DbSet<TableA> TableA { get; set; }
public virtual DbSet<TableB> TableB { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TableA>()
.HasOptional(e => e.JoinTable)
.WithRequired(e => e.TableA);
modelBuilder.Entity<TableB>()
.HasMany(e => e.JoinTable)
.WithRequired(e => e.TableB)
.HasForeignKey(e => e.TableB_FK)
.WillCascadeOnDelete(false);
}
}

Code first mapping issue while using Table per Hierarchy (TPH)

For past few days we started to work on code first, as a part of that i faced one issue while doing mapping which i summarized here with different example. It would be more helpful if i got any solution
for that.
Below are the three entities
Course -> Entity holds information about course like fee, description etc and each course will
belong to some category or Type(i.e.. student, employee etc), It will refer to the person for whom registration is made with the help of ParentId and Type column
Example : To get the coursejoined by the student with ID 10, the sql query would be
"select * from course where type=1 and parentid=10"
Student : CourseJoined entity here should fetch only related record of type student (ie..1) same apply for Employee too
So first how to achieve that first?
I have tried to implement TPH logic here as below but i am getting exception
public enum CourseType
{
Student = 1,
Employee = 2
}
public class Course
{
public int ID { get; set; }
public int ParentID { get; set; }
public decimal Amount { get; set; }
public CourseType Type { get; set; }
public string Description { get; set; }
}
public class StudentCourse : Course
{
}
public class EmployeeCourse : Course
{
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentCourse> CourseJoined { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public string WorkingAs { get; set; }
public string Experiance { get; set; }
public virtual ICollection<EmployeeCourse> CourseJoined { get; set; }
}
Exception:
The foreign key component 'ParentID' is not a declared property on
type 'EmployeeCourse'. Verify that it has not been explicitly
excluded from the model and that it is a valid primitive property.
2) To avoid that error i removed the ParentID from Course table and
placed them in StudentCourse and EmployeeCourse after that i found below migration script
CreateTable(
"dbo.Course",
c => new
{
ID = c.Int(nullable: false, identity: true),
Amount = c.Decimal(nullable: false, precision: 18, scale: 2),
Type = c.Int(),
Description = c.String(),
ParentID = c.Int(),
ParentID1 = c.Int(),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Employee", t => t.ParentID, cascadeDelete: true)
.ForeignKey("dbo.Student", t => t.ParentID1, cascadeDelete: true)
.Index(t => t.ParentID)
.Index(t => t.ParentID1);
where two columns are created for course but i don't want two columns(t.ParentID and t.ParentID1), I want only ParentID where i will insert student and employee id's .
Can anyone guide me to fix the above or any suggestion to implement the above scenario ?
I am using EF 6.0 version.