I have a database and I want to know how to map the relationships via code. I'm not sure if I understand exactly how this works.
Suppose I have two classes:
public class Address
{
[Key]
public int AddressID {get;set;}
public String Street {get;set;}
}
public class Shipment
{
[Key]
public int ShipmentID {get;set;}
public int ShipToAddressID {get;set;}
public virtual Address ShipToAddress {get;set;}
}
I have a few questions:
Does the navigation property merely give me access to the dbset of Address?
It seems that is not the case. However, if not, how do I specify which property is the foreign key on which the relationship exists? eg: How do I tell this navigation property that it should match the Address entities based on the AddressID property ?
Again, I'm doing this all via code. So I'm mapping the properties in the OnModelCreating call in the context. So please make suggestions/provide answers with that in mind.
You are in need of the HasRequired, WithMany, and HasForeignKey configuration methods.
EntityTypeConfiguration<Shipment> config = modelBuilder.Entity<Shipment>();
config
.HasRequired(s=>s.ShipToAddress)
.WithMany()
.HasForeignKey(s=>s.ShipToAddressID)
.WillCascadeOnDelete(false);
Related
I have a problem with the Entity Framework that I can't figure out.
I have a Module class that links to another Module (a one-to-one relationship) in two ways.
Code:
public class Module {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id {get;set;} /* primary key */
public Guid? OtherModule1 {get;set;}
[ForeignKey("OtherModule1")]
public Module OtherModule {get;set;}
public Guid? OtherModule2 {get;set;}
[ForeignKey("OtherModule2")]
public Module OtherModule2 {get;set;}
}
This gives the error that the principal end of the association cannot be determined.
Unable to determine the principal end of an association between the types 'Module' and 'Module'
I understand what the error means, but here's the thing. The relationship with OtherModule1 has always existed without a problem. This code works:
public class Module {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id {get;set;} /* primary key */
public Guid? OtherModule1 {get;set;}
[ForeignKey("OtherModule1")]
public Module OtherModule {get;set;}
}
There is no Fluent configuration present on this table
Now, if I add a new column, OtherModule2 and link it in the exact same way, this error pops up.
Does anyone have an idea of how to handle this?
TL/DR: one table has two foreign keys to the same table. One foreign key is handled correctly while the other is not.
The working model by convention defines one-to-many unidirectional (with only navigation property at the many side) relationship. It's equivalent of the following fluent configuration:
modelBuilder.Entity<Module>()
.HasOptional(e => e.OtherModule)
.WithMany()
.HasForeignKey(e => e.OtherModule1);
When you add a second FK / navigation property pair (I've renamed the FK property because you can't have 2 properties with the same name in the class):
public class Module
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; } /* primary key */
public Guid? OtherModule1 { get; set; }
[ForeignKey("OtherModule1")]
public Module OtherModule { get; set; }
[Column("OtherModule2")]
public Guid? OtherModule2_Id { get; set; }
[ForeignKey("OtherModule2_Id")]
public Module OtherModule2 { get; set; }
}
EF cannot automatically determine the relationships - it could be 2 unidirectional one-to-many or 1 bidirectional one-to-one, hence the error.
There is no way to specify that with data annotations in this case, so you need to use fluent configuration to either fully configure the relationships, or when combined with data annotations, to simply specify the cardinality and the navigation properties involved.
The following fluent configuration is sufficient to resolve the issue for the above data annotated model:
modelBuilder.Entity<Module>()
.HasOptional(e => e.OtherModule)
.WithMany();
modelBuilder.Entity<Module>()
.HasOptional(e => e.OtherModule2)
.WithMany();
I have an existing entity User. Now I am trying to create a new entity Contact with 0-1 relation with User.
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public int? UserId {get; set;}
public virtual User TheUser{get; set;}
}
All suggestion involve about something like this:
modelBuilder.Entity<User>()
.HasOptional(t => t.TheUser)
.WithOptionalDependent(u => u.TheConatct);
But this means we have to add TheConatct property to the existed User entity. Actually I do not want to make any modification to the existed entity. All what I need to define a foreign key form Contact to User entity and can access the User entity from Contact via TheUser property.
Update:
If I use ForeignKey attributes to annotate the property:
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public int? UserId {get; set;}
[ForeignKey("UserId")]
public virtual User TheUser{get; set;}
}
Then, the result of ObjectContext.CreateDatabase() will also include create statements for already existed tables (depending on the entities that have relations with User).
Of course we are talking about "Entity Framework 6 Code First", Also, I have the same problem with 1-1 relation.
The idea, I cannot alter the existing entity User to add additional property for the new entity Contact
I wonder if there is a way to overcome this issue
Just use another overload:
modelBuilder.Entity<User>()
.HasOptional(t => t.TheUser)
.WithOptionalDependent();
I don't know which version of Entity Framework you are using and I assume you are using Code First, but you may have to consider using a one to many relationship instead of a 0-1.
I don't believe there is support for 0-1 in the way you want it, but you can simulate by having a one to many (even though your "many" will only ever by 1)
I have an entity which is already being used with an underlying database, and it was created with just the navigational property to an optional entity (1:0..1). So by default conventions, EF created a nullable foreign key column in the DB and gave it the "MyProp_Id" name with underscore, according to that convention.
Now, I wish to expose that foreign key as a property on the entity, because it will make certain scenarios easier for me. I don't want to rename/change the underlying foreign key column in the DB (the MyProp_Id one). In fact, there shouldn't be any underlying DB updates, I just want to expose that FK on the entity. A code sample to clarify:
public class MyEntityA
{
public long Id { get; set; }
//public long? MyOptionalEntityB_Id { get; set; } <== this is what I am trying to add
//public long? MyOptionalEntityBId { get; set; } <== this didn't work either
public MyEntityB MyOptionalEntityB { get; set; }
}
I've tried just simply adding the "MyOptionalEntity_Id" property as property on the entity, hoping that EF would "automagically" see that because the names are the same, it would just map and be happy. NO DICE.
Then I tried to name my property "MyOptionalEntityId" (no underscore), but still NO DICE.
Then I tried adding an explicit mapping configuration to say:
this.Property(p => p.MyOptionalEntityId).HasColumnName("MyOptionalEntity_Id");
NO DICE
Is there a way to do this? Is this clear and make sense?
Try adding foreign key attribute.
public long? MyOptionalEntityB_Id { get; set; }
[ForeignKey("MyOptionalEntityB_Id")]
public MyEntityB MyOptionalEntityB { get; set; }
Or using fluent api.
modelBuilder.Entity<MyEntityA >()
.HasOptional(x => x.MyOptionalEntityB)
.WithMany().HasForeignKey(x => x.MyOptionalEntityB_Id);
// ^^^ -> if MyEntityB has collection of MyEntityA, mention it
I have these classes:
public class SystemRequirements : DbEntity
{
public string OS {get;set;}
}
public class Application : DbEntity
{
public string Name {get;set;}
public virtual SystemRequirements MinimumSystemRequirements {get;set;}
public Guid MinimumSystemRequirementsId {get;set;}
public virtual SystemRequirements RecommendedSystemRequirements {get;set;}
public Guid RecommendedSystemRequirementsId {get;set;}
}
I got an error saying: Introducting Foreign Key Constraint may cause cycles or multiple cascade paths.
DbEntity is an abstract class containing the Primary Key. [Key] public Guid Id {get;set;}
So, I changed Application to:
public class Application : DbEntity
{
public string Name {get;set;}
public virtual SystemRequirements MinimumSystemRequirements {get;set;}
[ForeignKey("MinimumSystemRequirements")]
public Guid MinimumSystemRequirementsId {get;set;}
public virtual SystemRequirements RecommendedSystemRequirements {get;set;}
[ForeignKey("RecommendedSystemRequirements")]
public Guid RecommendedSystemRequirementsId {get;set;}
}
So my question is why doesn't this work? I even tried putting a reference to Application in SystemRequirements, that didn't work?
Please don't trawl this post for spelling mistakes. My code is fine on VS, I copy and paste rather type out.
SystemRequirements probably needs to have the MinimumSystemRequirementsID and RecommendedSystemRequirmentsID fields. My understanding of EF FK relationships is it looks at the model for the value of the Element you are calling a FK and then uses that for linking.
Try this:
public class SystemRequirements : DbEntity
{
public string OS {get;set;}
public Guid MinimumSystemRequirementsId {get;set;}
public Guid RecommendedSystemRequirementsId {get;set;}
}
You have to tell EF that (at least) one of the associations between Application and SystemRequirements has no cascading delete, for instance:
modelBuilder.Entity<Application>().HasRequired(a => a.MinimumSystemRequirements)
.WithMany().HasForeignKey(a => a.MinimumSystemRequirementsId)
.WillCascadeOnDelete(false);
(In the context's overload of OnModelCreating).
Heyyyyy good news. I figured out the issue!
I remember back in the day (couple of months ago) I was told that in order to enable Cascade On Delete using Attributes in Code First you must do this:
public Guid CascadeOnId {get;set;}
public virtual Cascade CascadeOn {get;set;}
However before I knew that I was always using this:
public virtual Cascade CascadeOff {get;set;}
The reason I'm getting the cyclic delete, is because the first example will delete the HDD if it exists, but the HDD must always exist for the second instance of it, example:
public Guid FirstCascadeId {get;set;}
public virtual Cascade FirstCascade {get;set;}
public Guid SecondCascadeId {get;set;}
public virtual Cascade SecondCascade {get;set;}
So, entity framework is worrying that if you delete a cascade entity, it must delete it's associated parent, in deleting a parent you must delete all associated cascades. Which is where the cycle begins. I hope I'm making some sort of sense.
In order to turn Cascade On Delete off, you mustn't specify a Guid Id relating to that virtual. So In order to make the above work, you use:
public virtual Cascade FirstCascade {get;set;}
public virtual Cascade SecondCascade {get;set;}
You can turn on the first Cascade On Delete if you want, just make sure one of them hasn't got it and it works fine :)
I found the answer by using Model First, and then getting a massive glowing bulb above my head :P
My model looks like this
public Class Address
{
public int Id {get;set;}
/*Props here*/
}
public Class Person
{
public int Id {get;set;}
public String Name {get;set;}
[Required]
public Address Address{get;set;}
/*More props*/
}
Now suppose i have created a person with proper address, in future when i try to update person like this
var person= db.Persons.FirstOrDefault(p=>p.Id=1234);
person.Name="Foo";
db.SaveChanges();
It gives error saying Address is required.
So to avoid this iam including Address property too while loading Person Entity
var person= db.Persons.Include(p=>p.Address).FirstOrDefault(p=>p.Id=1234);
person.Name="Foo";
db.SaveChanges();
Is there any way i can update person Without including Address.
It's the model validation of DbContext which complains apparently. So, one solution would be to switch off this validation:
dbContext.Configuration.ValidateOnSaveEnabled = false;
The other option is to introduce a foreign key property:
public class Person
{
public int Id {get;set;}
public String Name {get;set;}
public int AddressId {get;set;}
public Address Address {get;set;}
/*More props*/
}
You can omit the [Required] attribute here because EF will detect the relationship as required by convention (due to the non-nullable FK property). This works also with enabled validation.
The behaviour is a bit confusing since EF doesn't send a change of the FK column to the database, so there is not really a constraint violation and the Update command executes fine. I guess that the validation just checks the state of the model in memory (invalid, because Address is null) and not the state the model would have in the database when SaveChanges did execute (valid, because FK is correctly set).
If you want the address to be automatically loaded by EF 4.1 you have to make the Address-porperty virtual:
public virtual Address Address{get;set;}
EF will then lazy-load the address when needed.