I'm having trouble with reverse navigation on one of my entities.
I have the following two objects:
public class Candidate
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long CandidateId { get; set; }
....
// Reverse navigation
public virtual CandidateData Data { get; set; }
...
// Foreign keys
....
}
public class CandidateData
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long CandidateDataId { get; set; }
[Required]
public long CandidateId { get; set; }
// Foreign keys
[ForeignKey("CandidateId")]
public virtual Candidate Candidate { get; set; }
}
Now my foreign key navigation on the CandidateData object works fine. I am having trouble getting the reverse navigation for the candidate object to work (if that's even possible).
This is my OnModelCreating function:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Candidate>()
.HasOptional(obj => obj.Data)
.WithOptionalPrincipal();
base.OnModelCreating(modelBuilder);
}
It's close to working except in the database I get two columns that link to the CandidateId. I get the one I from the POCO object the I get another column Candidate_CandidateId I assume was created by the modelBuilder.
I am quiet lost at the moment. Can someone please shed some light on what's going on?
The One to One problem....
The issue is EF and CODE First, when 1:1 , for the dependent to have a Primary key that refers to the principal. ALthough you can define a DB otherwise and indeed with a DB you can even have OPTIONAL FK on the Primary. EF makes this restriction in Code first. Fair Enough I think...
TRy this instead: IS have added a few opinions on the way which you may ignore if you disagree:-)
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace EF_DEMO
{
class FK121
{
public static void ENTRYfk121(string[] args)
{
var ctx = new Context121();
ctx.Database.Create();
System.Console.ReadKey();
}
}
public class Candidate
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]// best in Fluent API, In my opinion..
public long CandidateId { get; set; }
// public long CandidateDataId { get; set; }// DONT TRY THIS... Although DB will support EF cant deal with 1:1 and both as FKs
public virtual CandidateData Data { get; set; } // Reverse navigation
}
public class CandidateData
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] // best in Fluent API as it is EF/DB related
public long CandidateDataId { get; set; } // is also a Foreign with EF and 1:1 when this is dependent
// [Required]
// public long CandidateId { get; set; } // dont need this... PK is the FK to Principal in 1:1
public virtual Candidate Candidate { get; set; } // yes we need this
}
public class Context121 : DbContext
{
static Context121()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context121>());
}
public Context121()
: base("Name=Demo") { }
public DbSet<Candidate> Candidates { get; set; }
public DbSet<CandidateData> CandidateDatas { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Candidate>();
modelBuilder.Entity<CandidateData>()
.HasRequired(q => q.Candidate)
.WithOptional(p=>p.Data) // this would be blank if reverse validation wasnt used, but here it is used
.Map(t => t.MapKey("CandidateId")); // Only use MAP when the Foreign Key Attributes NOT annotated as attributes
}
}
}
I think that the foreign key should be created as:
.Map(t => t.MapKey("CandidateDataId")) because thsi foreign key will be placed in Candidate table...
Waht do you think?
Related
I am getting the multiple cascade path error.
My models are 0..1 to 0..1
Or at least that is what i am trying to achieve.
My models look like this:
public class House
{
public Guid Id { get; set; }
[ForeignKey("User")]
public User Tenant { get; set; }
public class User
{
public House Home { get; set; }
public Guid Id { get; set; }
I have also this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<House>()
.HasOne(p => p.Tenant)
.WithOne(t => t.Home)
.OnDelete(DeleteBehavior.Restrict);
}
The error is not that i get an error when i delete a object, it simply will not create the DB at all.
I have already been told that this has been solved here:
Entity Framework Core cascade delete one to many relationship
However i am afraid to say that it does not appear to really address my issue, or if it does i do not understand how it solves it.
Any help would be greatly appreciated.
//update
Just to clarify. There are many users/tenants and many houses/homes.
Each of the tenants can have a house but does not always.
Each of the homes can have a tenant but does not always have one.
Try a very simple thing just to be sure when you start getting errors :
public class User{
public Guid Id{get; set;}
public string UserName{ get; set;}
}
public class House{
public Guid Id {get; set;}
public string StreetName {get; set;}
public UserId {get; set;} //this should generate a foreign key to the User table
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<House>().Property(c => c.UserId).IsRequired(false);
}
This should work to create houses that doesn't necessarely requires Users to exist.
So 1 house can have only 1 tenant. And 1 user can stay in only 1 home. Your relationship is 1 to 1. In this case, you can put foreign key in either table, but only 1 table. Then, you don't have issue of cycle cascade.
I suggest to put HouseId in User class, so that you can extend your app to allow 1 house to have many tenants easily later.
Here, one foreign key is declared :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<House>()
.HasOne(p => p.Tenant)
.WithOne(t => t.Home)
.OnDelete(DeleteBehavior.Restrict);
}
House.Tenant is linked to User.Home.
The tenant is the resident. Not what you want.
You need declare two foreign key distinct :
Fluent syntax
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasOne(u => u.Home).WithOne();
modelBuilder.Entity<House>().HasOne(h => h.Tenant).WithOne();
}
Attribute syntax
public class House
{
public Guid Id { get; set; }
[ForeignKey("TenantId")]
public User Tenant { get; set; }
}
public class User
{
[ForeignKey("HomeId")]
public House Home { get; set; }
public Guid Id { get; set; }
}
It turns out that Ivan was correct in one of his comments above. The issue was not actually in the code itself. In fact even simpler code will work in this case.
The error was caused by the migrations. I had to remove all migrations and start from scratch. Clearly some of the older model designs clashed with this new addition in a way that it was simply not possible to move forward. The databases had to be recreated from scratch.
Thank you everyone for your help. Especially Ivan and David.
Here are the simple models that do what i needed to do:
public class Home
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? TenantId { get; set; }
public Tenant Tenant { get; set; }
}
public class Tenant
{
public Guid Id { get; set; }
public string Name { get; set; }
public Home Home { get; set; }
}
I do not even have to override the OnModelCreating.
I have a very simple model:
class Bag
{
public int Id { get; set; }
public ICollection<RandomThing> RandomThings { get; set; }
}
class RandomThing
{
public int Id { get; set; }
public String Description{ get; set; }
}
My context's OnModelCreating (in EF 6) is something like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Bag>()
.ToTable("Bag")
.HasMany(x => x.RandomThings).WithRequired(x => x.Bag).WillCascadeOnDelete();
modelBuilder.Entity<RandomThing>().ToTable("RandomThing");
}
As far as I can tell, there is no equivalent way to do this. I know that cascade deletes are not supported. However, I was wondering, what is the best way to model the one to many relationships as defined in my code example? It seems like all of that stuff has gone (or is yet to be implemented).
I have seen an example (here) of a DbContext exactly like how I want, but it's actually wrong and doesn't work. If I try to pull down that source code, set up my environment, and do a save on a blog entity with posts, those posts don't get saved as one might imagine.
Are there any workarounds for this?
Update: This answer was written for EF7 beta7. APIs have since changed. See http://docs.efproject.net/en/latest/modeling/relationships.html for the latest on using EF Core.
Original Answer
In EF 7, you can configure one to many relationships in the OnModelCreating method. For this to work, add a property to RandomThing to represent the forign key, and also a CLR reference to the parent type.
class Bag
{
public int Id { get; set; }
public ICollection<RandomThing> RandomThings { get; set; }
}
class RandomThing
{
public int Id { get; set; }
public String Description{ get; set; }
public Bag Bag { get; set; }
public int BagId { get; set; }
}
In your context, configure the relationship with the following setup:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RandomThing>()
.Reference(e => e.Bag)
.InverseCollection(p => p.RandomThings)
.ForeignKey(e => e.BagId);
}
Side note: When EF 7 plans to add support for configuring this via attributes instead.
although I haven't used EF7 at all, in EF6 I would make your models look like this:
class Bag
{
public int Id { get; set; }
public virtual ICollection<RandomThing> RandomThings { get; set; }
}
class RandomThing
{
public int Id { get; set; }
public String Description{ get; set; }
public int BagId {get; set;}
[ForeignKey("BagId")]
Public Bag Bag {get; set;}
}
this way the relationship is taken care of with data annotations and you don't need the model builder for this specific task
I have two classes (Person and Phone) and I didn't figure out how to create a relationship between these classes without create a navigation property.
Is there some way to do that? Or I HAVE to create a navigation property?
Writing in T-SQL, I'm looking for somethink like that:
ALTER TABLE [dbo].[PHONE] WITH NOCHECK
ADD CONSTRAINT [RelationName1] FOREIGN KEY
( [PERSONID] )
REFERENCES [dbo].[PERSON]
( [PERSONID] )
In example below, I don't know how to write this sentence:
modelBuilder.Entity<Person>().HasMany<Phone>().... ?????:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
public class Phone
{
public int PhoneId { get; set; }
public int PersonId { get; set; }
public string PhoneNumber { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Phone> Phones { get; set; }
public MyContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().HasMany<Phone>()...?????
}
}
Thank you!
You do, I think, need a navigation property in at least one direction. If this is causing serialization problems, have you investigated adding attributes to prevent it from being serialized? For example, add the DataContractAttribute on your classes, and add DataMemberAttribute to all the properties that you do want to serialize.
This would give you the following (in which I use attributes, but you could use fluent config instead):
[DataContract]
public class Person
{
[Key]
[DataMember]
public int PersonId { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Phone
{
[Key]
[DataMember]
public int PhoneId { get; set; }
[DataMember]
public int PersonId { get; set; }
// No [DataMember] here.
[ForeignKey("PersonId")]
public Person Person { get; set; }
[DataMember]
public string PhoneNumber { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Phone> Phones { get; set; }
public MyContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// If I had a Phones collection on Person, I could use the other override
// of WithMany.
modelBuilder.Entity<Phone>().HasRequired(q => q.Person).WithMany();
}
}
one way (the only in fact, referring to first comment of your question) I imagine to reach the goal is by overriding the initializer and sending sql command from the seed method of the initializer. Something like:
public class CreateDatabaseIfNotExistsWithSeedData : CreateDatabaseIfNotExists<SomeContext> {
protected override void Seed(SomeContext context) {
base.Seed(context);
context.Database.ExecuteSqlCommand("ALTER TABLE [dbo].[PHONE] WITH NOCHECK ADD CONSTRAINT [RelationName1] FOREIGN KEY ( [PERSONID] ) REFERENCES [dbo].[PERSON] ( [PERSONID] )");
}
}
Based on your comments, following I can suggest
Problems to Serialize those classes using WebApi/JSON :
well, it is a serious problem when you try to serialize any entity having many-to-many (circular) relationship. Even Ignore Circular Reference (as provided by JSON.NET Serializer) doesn't work. The best that you can do is to, instead of serializing your exact entity, you can serialize a Linq .Select list of this entity i.e. Select only scalar properties or ignore circular and not required navigational properties from the Select list and then serialize it.
I want to avoid EF to insert child informations in SaveChanges
What you mean? If you have a foreign key in your table, you got to have a master table with a primary key, to which this foreign key(PersonId) refers to right? That means, you have to have a table PersonMaster or similar with or without data. Entity framework does it simple for you. If you meant, you don't want to insert new data in your PersonMaster, then simply don't add a new Person object to your Phone entity, find the Person (from the same context ) and then add it. EntityFramework will simply use the primary key.
If you tried to say, you want to have a value in your personId column, which doesn't exist anywhere else and still be the foreign key, that's not possible you know that right? Plain SQL.
I'm defining a many-to-many relationship as follows:
modelBuilder.Entity<GameSessionEntry>().
HasMany(c => c.Users).
WithMany(p => p.GameSessionEntries).
Map(
m =>
{
m.MapLeftKey("SessionId");
m.MapRightKey("UserId");
m.ToTable("UserSessions");
});
However, I keep getting:
The Foreign Key on table 'UserSessions' with columns 'UserId' could
not be created because the principal key columns could not be
determined. Use the AddForeignKey fluent API to fully specify the
Foreign Key.
I'm new to database work and the EntityFramework in general - what is it asking me to do?
It's the recurring confusion with left and right, see this explanation by Slauma. So you just have to turn around the key names:
m.MapLeftKey("UserId"); // Property in the HasMany call
m.MapRightKey("SessionId"); // Property in the WithMany call
This is how I usually go about creating a many to many table (note this requires no fluent api configuration)
public class User
{
public int Id { get; set; }
public virtual ICollection<UserSession> UserSessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public virtual ICollection<UserSession> UserSessions { get; set; }
}
public class UserSession
{
[Key]
[Column(Order = 1)]
public int UserId { get; set; }
[Key]
[Column(Order = 2)]
public int SessionId{ get; set; }
public virtual User User { get; set; }
public virtual Session Session { get; set; }
}
Instead of fiddling around with a many-many relationship you should rewrite it to a weak entity set.
If you have for instance this relationship:
You can redesign it to a weak entity set:
By doing this you get rid of the many-many relationship and don't have to store the same data in multiple tables.
For more information: http://fileadmin.cs.lth.se/cs/Education/EDA216/lectures/dbtoh4.pdf
Read the lecture slides about "The Relational Data Model" starting on slide 87/360.
I've been trying to get EF4 CTP5 to play nice with an existing database, but struggling with some basic mapping issues.
I have two model classes so far:
public class Job
{
[Key, Column(Order = 0)]
public int JobNumber { get; set; }
[Key, Column(Order = 1)]
public int VersionNumber { get; set; }
public virtual User OwnedBy { get; set; }
}
and
[Table("Usernames")]
public class User
{
[Key]
public string Username { get; set; }
public string EmailAddress { get; set; }
public bool IsAdministrator { get; set; }
}
And I have my DbContext class exposing those as IDbSet
I can query my users, but as soon as I added the OwnedBy field to the Job class I began getting this error in all my tests for the Jobs:
Invalid column name 'UserUsername'.
I want this to behave like NHibernate's many-to-one, whereas I think EF4 is treating it as a complex type. How should this be done?
UserUsername is the default name that EF Code First chooses for the foreign key in the Jobs table. Obviously, it's not the same name that your existing database has for the FK column in Jobs table. You need to override this conventions and change the FK's default name so that it matches your database. Here is how it's done with fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Job>()
.HasOptional(j => j.OwnedBy)
.WithMany()
.IsIndependent()
.Map(m => m.MapKey(u => u.Username, "TheFKNameInYourDB"));
}
Try let it create new database from your schema and look what columname it expect.
I think its foreing key for OwnedBy. It tries to name it according to its internal convention, that is incompatible with how you named your foreing key column. And no, its really treating it as many-to-one.