I have two entities with following relationShip (these entities are taken for example purpose only)
public class Entity
{
public long ID { get; set; }
}
public class Doctor : Entity
{
public string Name {get; set;}
public string sprcialization { get; set;}
public string Icollection<JrDoctor> childDoctors { get; set;}
}
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
this relationship in entityframework is creating an extra column Doctor_Id in JrDoctor table. Why is it so? and how can I avoid it using data annotations.
Here is how EF works - if it sees navigation property (Doctor in your case), then EF understands that both entities are related to each other. Relation in database is defined by foreign keys. So EF generates foreign key with name PropertyName_KeyColumnOfRelatedEntity. That's why you see column Doctor_Id in JrDoctor table.
If you don't want default generated foreign key column, then you should tell EF what it should use instead. That is done via data annotations attributes or fluent configuration. I prefer latter one:
modelBuilder.Entity<JrDoctor>()
.HasOptional(jd => jd.Doctor)
.WithMany(d => d.childDoctors)
.HasForeignKey(jd => jd.DoctorId); // here you tell which column is FK
Data annotations require modification of entity classes. In your case you should add attribute which tells name of FK for navigation property, just as you did for JuniorDoctor:
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
[ForeignKey("DoctorId")]
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
InverseProperty did the trick.
public class Entity
{
public long ID { get; set; }
}
public class Doctor : Entity
{
public string Name {get; set;}
public string sprcialization { get; set;}
[InverseProperty("Doctor")]
public string Icollection<JrDoctor> childDoctors { get; set;}
}
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
[ForeignKey("DoctorId")]
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
Related
I'm new to EF and I'm facing the following situation with my model.
I have the following entities:
public class ForwardingConstruct
{
public int Id {get; set;}
public virtual ICollection<FcPort> FcPorts { get; set; }
/// Other attributes
}
[Owned]
public class FcPort
{
public virtual ICollection<LogicalTerminationPoint> Ltps { get; set; }
/// Other attributes
}
public class LogicalTerminationPoint
{
public int Id {get; set;}
/// Other attributes
}
Based on this answer I know that it is posible to map a one-to-one relation from the owned entity, but my question is if it is posible to create a one-to-many reference from the same entity
Edit: Initially I forgot to mention that I'm using code-first approach and Entity Framework Core.
public class ForwardingConstruct
{
public int Id {get; set;}
[InverseProperty("ForwardingConstruct")]
public virtual ICollection<FcPort> FcPorts { get; set; }
/// Other attributes
}
[Owned]
public class FcPort
{
[InverseProperty("FcPort")]
public virtual ICollection<LogicalTerminationPoint> Ltps { get; set; }
public int ForwardingConstructId { get; set; }
public virtual ForwardingConstruct ForwardingConstruct { get; set; }
/// Other attributes
}
public class LogicalTerminationPoint
{
public int Id {get; set;}
public int FcPortId { get; set; }
public virtual FcPort FcPort { get; set; }
}
You should specify the foreign key in your tables. LogicalTerminationPoint.FcPortId is what tells to entity framework to create a relationship.
Then with the InverseProperty attribute you can specify which property EF can use to load the relevant children.
Say I have the following models in my database:
public class LetterEntity
{
public int Id {get; set;}
public string Content {get; set;}
public List<Destination> Destinations {get; set;}
public virtual Folder Folder {get; set;}
public int FolderId {get; set;}
}
Now I want to add a new letter the client has made to my database:
public class SendLetterRequest
{
public string Content {get; set;}
public List<int> DestinationsIds {get; set;}
}
public void SaveLetterToDatabase(SendLetterRequest letter)
{
var letterEntity = new LetterEntity
{
Content = letter.Content;
FolderId = 1;
// How to insert the Destinations Ids in a way that I don't have to load all of those destinations to the context?
}
context.Set<LetterEntity>().Add(letterEntity);
context.SaveChanges();
}
I know that if a LetterEntity only had a single Destination object I could just set it's foreign key value and the insert would work (Just like I do with the FolderId).
How is it done when working with List of entities - how to tell EF that those Ids are already in the database, without fetching all of them to the context, so that it doesn't recreate them?
EDIT:
My Destination model -
public void Destination
{
// Manual key
public int Address {get; set;}
public string DestinationName {get; set;}
public string Information {get; set;}
}
Well, as you probably know, there are two ways to define many-to-many replationship in EF.
(1) Implicit link table
This is what you have used. You create explicitly only the two entitities, define the relation via navigation properties/and or model configuration and let EF maintain the so called "link" table. It's easy, but the downside is that you don't have access to that table, so the only way to add related items is to actually load the entities needed and add them to the navigation property collection.
(2) Explicit link table
Here you define explicitly the link entity and configure 2 one-to-many relations. This way you have access and can add related records w/o having the other entities loaded.
For instance, in your case it could be something like this:
Model:
public class LetterEntity
{
public int Id { get; set; }
// ....
public List<LetterDestinationLink> Links { get; set; }
}
public class Destination
{
public int Id { get; set; }
// ....
public List<LetterDestinationLink> Links { get; set; }
}
public class LetterDestinationLink
{
[Key]
[Column(Order = 0)]
public int LetterId { get; set; }
[Key]
[Column(Order = 1)]
public int DestinationId { get; set; }
public LetterEntity Letter { get; set; }
public Destination Destination { get; set; }
}
Context:
public class YourDbContext : DbContext
{
public DbSet<LetterEntity> LetterEntities { get; set; }
public DbSet<Destination> Destinations { get; set; }
public DbSet<LetterDestinationLink> LetterDestinationLinks { get; set; }
}
Use case:
List<int> destinationIds = ...;
var letterEntity = new LetterEntity { ... };
letterEntity.Links = destinationIds.Select(destinationId =>
new LetterDestinationLink { Letter = letterEntity, DestinationId = destinationId })
.ToList();
context.Set<LetterEntity>().Add(letterEntity);
context.SaveChanges();
I'm trying to create a linked table that will allow me to have a many to many relationship between my product and accessory tables.
My classes are like this:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Accessory> Accessories { get; set; }
}
public class Accessory {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Machine> Machine { get; set; }
}
public class Type {
public int Id { get; set; }
public string Name { get; set; }
}
The same accessory can be on a product more than once if it is a different type, which will be determined in the link table. Something like this:
public class ProductAccessoryLink {
public int productId {get; set;}
public int accessoryId {get; set;}
public int typeId {get; set}
public int sort {get; set;}
public string notes {get; set}
}
Is this the right approach.
EDIT
This is the error I'm getting when I run update-database:
Introducing FOREIGN KEY constraint
'FK_dbo.ProductAccessoryLinks_dbo.Types_TypeId' on table
'ProductAccessoryLinks' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints. Could not create constraint. See previous
errors.
This is the sql causing the error: ALTER TABLE
[dbo].[ProductAccessoryLinks] ADD CONSTRAINT
[FK_dbo.ProductAccessoryLinks_dbo.Types_TypeId] FOREIGN KEY ([TypeId])
REFERENCES [dbo].[Types] ([Id]) ON DELETE CASCADE
In your case you need to map explicitly the junction table. Your model would be like this:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductAccessoryLink> ProductAccessoryLinks { get; set; }
}
public class Accessory
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductAccessoryLink> ProductAccessoryLinks { get; set; }
}
public class Type
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductAccessoryLink
{
public int ProductId { get; set; }
public int AccessoryId { get; set; }
public int TypeId { get; set; }
public int sort { get; set; }
public string notes { get; set; }
public virtual Type Type { get; set; }
public virtual Product Product { get; set; }
public virtual Accessory Accessory { get; set; }
}
And you could configure the relationships overriding the OnModelCreating method on your context this way:
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductAccessoryLink>().HasKey(i => new { i.ProductId, i.AccesoryId, i.TypeId});
modelBuilder.Entity<ProductAccessoryLink>()
.HasRequired(i => i.Product)
.WithMany(k => k.ProductAccessoryLinks)
.HasForeignKey(i=>i.ProductId);
modelBuilder.Entity<ProductAccessoryLink>()
.HasRequired(i => i.Accesory)
.WithMany(k => k.ProductAccessoryLinks)
.HasForeignKey(i=>i.AccesoryId);
modelBuilder.Entity<ProductAccessoryLink>()
.HasRequired(i => i.Type)
.WithMany()
.HasForeignKey(i=>i.TypeId);
}
EF lets you configure directly many-to-many relationships of the way you were attempting to. Thereby EF is responsible for build a join table in the database with the appropriate keys of the tables it’s joining. (The keys are both primary keys of the join table and foreign keys pointing to the joined tables). That lets you to get your data across the join table without you having to be aware of its presence. But when you want to personalize that table (adding, for example, some additional properties), you need to map it explicitly as I show above.
Update
That exception is caused when you have multiple paths of cascade deletes that could end trying to delete the same row in the Types table. To resolve that problem I recommend you check my answer in this post
I have one big question about TPT + EF6.
At my DB model I have one table Person (basic information of persons in my application) and I have tables for Supplier and Consumer.
My classes are:
//to table dbo.Person
public class Person
{
public long Id {get; set;} //is pk
public string Name {get; set;}
}
//to table dbo.Supplier
public class Supplier : Person
{
public long Id {get; set;}//is pk and fk
public string ProductName {get; set;}
}
//to table dbo.Consumer
public class Consumer
{
public long Id {get; set;} //is pk and fk
public string budget {get; set;}
}
If I have one person that is both supplier and consumer, and I get the records from same/different DBContext or navigate form another Entitys, then EF throws an exception:
All objects in the EntitySet Person must have unique primary keys. However, an instance of type Supplierand an instance of type Consumer both have the same primary key value, EntitySet=Person;ID=20.
Is there a way to specify one discriminator in TPT inheritance?
How do I solve this issue?
I suggest the pattern you actually need is Table Per Concrete Class
This can be achieved by
public class Person
{
public int Id { get; set; }
public Supplier Supplier { get; set; }
public Consumer Consumer { get; set; }
}
public class Supplier
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class Consumer
{
public int Id { get; set; }
public string Budget { get; set; }
}
Remember to put the following in your dbcontext
public DbSet<Supplier> Suppliers { get; set; }
public DbSet<Consumer> Consumers { get; set; }
public DbSet<Person> People { get; set; }
I am using EF Model first to create two entities
public class Brief
{
public int Id { get; set; }
public string Title { get; set; }
public string tId {get; set;}
public int SpeakerId { get; set; }
}
public class Speaker
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
What I want to do is in Brief entity decorate tId field as Unique.
Second when I run entities as it is, it creates the database but it does not create foreigh key relation between SpeakerId in Briefs table and Speakers
Please let me know how
1. Decorate tId as unique
2. Why it is not creating the foreign key relation on SpeakerId and Speakers table?
Thanks
For problem number 2 you need to add a navigational property to your entity:
public class Brief
{
public int Id { get; set; }
public string Title { get; set; }
public string tId {get; set;}
public int SpeakerId { get; set; }
//Navigational property
public virtual Speaker Speaker { get; set;} 1 Brief has 1 Speaker
}
Depending on the Relationship this can also be public virtual ICollection<Speaker> Speakers { get; set;} Vice Versa for the Speaker entity:public virtual Brief Brief { get; set;} ` or the ICollection for n:m / 1:m relations
The unique constraint on non key columns should not be implemented as of yet based on http://entityframework.codeplex.com/workitem/299
Further reading / related questions:
Setting unique Constraint with fluent API?
http://bit.ly/OcE2HV
See Unique key with EF code first
Dependent on the EF version you can set an attribute on the property.
Use a navigational property so EF can determine the relation.
Note that the virtual keyword denotes lazy loading. See Entity Framework Code First Lazy Loading