I have Item table which have manyTomany to itself:
public virtual ICollection<Item> Related { get; set; }
modelBuilder.Entity<Item>().HasMany(x => x.Related ).WithMany();
Data:
Item_Id Item_Id1
8 2
8 3
8 4
How can I allow duplicate , meaning allow another 8-2 for example, when I try to insert another "duplicate" data , it shows only 1.
I think that the origin of the problem is to misunderstand what an entity is. An entity is unique, and has a unique Id, so for example, a company could have a collection of employed people, that are unique, so you cannot have two instances of the same person in the same collection. If you have a shopping cart, you have to think to the items in the collection as 'order lines' and not as the items itself. Any order line has its own Id and a reference to an entity, that is the object you are purchasing.
I didn't check this specifically, so you may need to work out the details -
but (I think) the only way to do it is to manually define the index table (e.g. Item2Item).
Check this post I made for additional info
EF code-first many-to-many with additional data
That is on how to create a custom table for many-to-many - with adding
additional fields.
Now that you have a custom table, you need to change it a bit - you need to remove the composite keys from the .HasKey. Instead use your own pk, e.g. identity as for any other table.
i.e. you can define a unique PK, identity like for any other table - and have that as your key. That should let you have duplicate keys in your fk columns).
Check this post as it seems logically close (though not the same)
Many to many (join table) relationship with the same entity with codefirst or fluent API?
Well this gave me the final solution--> EF Code First, how can I achieve two foreign keys from one table to other table? , NSGaga thanks for the direction
public class Item : IValidatableObject
{
[Key]
public int ID { get; set; }
public virtual ICollection<ItemRelation> Related{ get; set; }
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (....)
{
yield return new ValidationResult("Name not valid", new[] { "Name" });
}
}
}
public class ItemRelation
{
[Key]
public int ID { get; set; }
public int ItemAID { get; set; }
public virtual Item ItemA { get; set; }
public int ItemBID { get; set; }
public virtual Item ItemB { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<ItemRelation>().HasRequired(c => c.ItemA)
.WithMany(m => m.Related)
.HasForeignKey(c => c.ItemAID)
.WillCascadeOnDelete();
modelBuilder.Entity<ItemRelation>().HasRequired(c => c.ItemB)
.WithMany()
.HasForeignKey(c => c.ItemBID)
.WillCascadeOnDelete(false);
}
public DbSet<Item> Items { get; set; }
public DbSet<ItemRelation> ItemsRelations { get; set; }
Data:
Id ItemA_Id ItemB_Id
1 8 2
2 8 3
3 8 5
4 8 5
5 8 5
6 1 6
7 5 4
8 2 9
Related
I'm attempting to map to a lookup table that contains values for multiple tables. I cannot modify the database. The table has a one-to-many relationship with multiple tables, but no foreign key constraints. The lookup table has three columns code, category and description. A sample set of the data would look like this:
Lookup table:
code
category
description
1
Role
Admin
2
Role
User
1
Job
Mechanic
2
Job
Plumber
3
Job
Electrician
1
Activity
Work
2
Activity
Overtime
3
Activity
Training
I'm interested in joining the Activity table that looks like this (some columns omitted for clarity):
Activity table:
id
code
hours
1
1
8.0
2
1
8.0
3
2
1.0
4
2
5.3
I want to join the Activity table to the Lookup table using EF Core. If I was writing a SQL statement I'd just have something like this:
SELECT *
FROM Activity
JOIN Lookup ON Lookup.code = Activity.code
AND Lookup.category = 'Activity'
So in EF Core I created my classes to represent my tables:
public class Lookup
{
[Key]
public string Code { get; set; }
public string Category { get; set; }
public string Description { get; set; }
}
public class Activity
{
[Key]
public string Id { get; set; }
public string Code { get; set; }
public double Hours { get; set; }
[NotMapped]
public string LookupCategory { get; set; } = "Activity";
public Lookup ActivityType { get; set; }
}
I also have the following fluent relationship setup:
modelBuilder.Entity<Activity>()
.HasOne<Lookup>(x => x.ActivityType)
.WithMany()
.HasForeignKey(x => x.Code);
But this doesn't work since there are multiple rows with the same code value. How do I get the key constraint to also take in to account the LookupCategory value since it is not mapped?
Following pjs's suggestions I was able to implement the following successfully:
I created a new class that extended Lookup. This class has the attribute NotMapped, which was an important part in getting everything to finally work.
[NotMapped]
public class ActivityType : StaffLookup{ }
In Activity I modified the class to use ActivityType instead of StaffLookup. I also removed the LookupCategory property
public class Activity
{
[Key]
public string Id { get; set; }
public string Code { get; set; }
public double Hours { get; set; }
// This is mapped to our subclass so that we can get the filtered values
public ActivityType ActivityType { get; set; }
}
In my OnModelCreating I removed the existing fluent relationship and added the following new ones:
// This tells EF what column is used as a the filter
modelBuilder.Entity<StaffLookup>()
.HasDiscriminator(x => x.Category);
// This is what filters the Lookup table for us
modelBuilder.Entity<ActivityType>()
.HasDiscriminator()
.HasValue("Activity");
// Since the foreign key isn't mapped in the database
// we need to add this relationship
modelBuilder.Entity<ActivityMain>()
.HasOne(x => x.ActivityType)
.WithMany()
.HasForeignKey(x => x.ActivityCode);
Look at setting up a Table-Per-Hierarchy, TPH, with Category as the discriminator. Then your EF model can have Lookup as the base table, with RoleType, ActivityType, etc. as child entities in your EF model. The relationship would be from ActivityType to Activity, and EF would already know that the only applicable values in ActivityType are the Lookup rows with Category = 'Activity'. You would want to verify all existing data meets those constraints since they aren't enforced in the database.
I'm a relatively new programmer and I'm in the middle of creating Entities for Entity Framework Core for a project I'm currently on, but I'm having difficulty figuring out how to setup 1 specific entity named "Group".
The "Group" entity can contain zero to more groups as parents of it, and it can contain zero to more groups as its children.
i tried writing it the following way with the group class:
public class Group
{
public int GroupID { get; set; }
//Other properties here
public ICollection<SubGroup>? ParentGroups { get; set; }
public ICollection<SubGroup>? ChildGroups { get; set; }
}
with the SubGroup class looking like this:
public class SubGroup
{
public int ParentGroupID { get; set; }
public int ChildGroupID { get; set; }
public Group ParentGroup { get; set; }
public Group ChildGroup { get; set; }
}
alongside this piece of fluent API:
modelBuilder.Entity<SubGroup>().HasKey(sg => new { sg.ParentGroupID, sg.ChildGroupID });
modelBuilder.Entity<Group>().HasMany(g => g.ParentGroups).WithOne(sg => sg.ParentGroup).HasForeignKey(g => g.ParentGroupID).OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Group>().HasMany(g => g.ChildGroups).WithOne(sg => sg.ChildGroup).HasForeignKey(g => g.ChildGroupID).OnDelete(DeleteBehavior.Restrict);
The thought process in my head is that subgroups should show which groups are children/parents of which groups.
While i was able to generate a migration and update the database, i'm not entirely sure if this is how you're supposed to do it, so i've also thought about doing it the following way:
public class Group
{
public int GroupID { get; set; }
//Other properties here
public ICollection<Group>? ParentGroups { get; set; }
public ICollection<Group>? ChildGroups { get; set; }
}
alongside the following fluent API:
modelBuilder.Entity<Group>().HasMany(g => g.ChildGroups).WithMany(sg => sg.ParentGroups);
i want to ask anyone who's made this kind of relation before if i'm right or wrong in either case, and how it should be written. I hope anyone could help me here
Thanks in advance
EDIT: I completely forgot to mention in the first case that the only reason I could update the database there was because I added .OnDelete(DeleteBehavior.Restrict) to the end of the fluent API because it kept warning me with this:
Introducing FOREIGN KEY constraint 'FK_SubGroup_Group_ParentGroupID' on table 'SubGroup' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints
I want the group table rows to cascade and delete all subgroup relation rows associated with the deleted group without deleting the other end of the relation but don't know how in this case
I'm struggling a bit with an NHibernate mapping within a console app.
Here is the summary:
It's a one to many relationship. PARENT_TABLE (one) to CHILD_TABLE (many)
PARENT_TABLE has a composite key consisting of two keys: Vendor and
Invoice
Id(x => x.Invoice).Column("INVOICE");
References(x => x.Distribution).Column("INVOICE");
CHILD_TABLE has a composite key consisting of three: Vendor,
Invoice, and Num
Id(x => x.Invoice).Column("INVOICE");
HasOne(x => x.Invoice).ForeignKey("INVOICE").Cascade.Refresh();
When I run through some data that has the following example:
Vendor Invoice Num
10 | 44 | 1
11 | 44 | 1
11 | 44 | 2
I end up getting an error because of the duplicate ID in Invoice (Makes total sense)
Then, I tried using a composite ID using 3 keys for Distribution and 2 keys for Invoice. This, as I'm sure many of you already know, also ended up with an error since there are two records that use 11 and 44 for Vendor and Invoice, respectively.
My question: Is there a way to declare the relationship using a composite ID (INVOICE and VENDOR) and still have the child collection respect/enforce the uniqueness of the 3 key composite (INVOICE, VENDOR, and NUM)?
I've tried several permutations of composite keys and haven't been able to figure it out. Maybe it's just not designed for this. Any help would be much appreciated!
Given you have a composite id definition on your entities, you have to map it using the CompositeId method. In your child entity, you also have to define the references for key columns of your parent entity, because it is a composite id (and that's the reason where I personally do not enjoy working with composite IDs).
Considering your model:
public class ParentEntity
{
public virtual int Vendor { get; set; }
public virtual int Invoice { get; set; }
// other properties
}
public class ChildEntity
{
public virtual int Vendor { get; set; }
public virtual int Invoice { get; set; }
public virtual int Num { get; set; }
public virtual ParentEntity ParentEntity { get; set; }
}
Your mappings could be like this:
public class ParentEntityMap : ClassMap<ParentEntity>
{
public ParentEntityMap()
{
CompositeId()
.KeyProperty(x => x.Vendor, "VENDOR")
.KeyProperty(x => x.Invoice, "INVOICE");
// other mappings ...
}
}
public class ChildEntityMap : ClassMap<ChildEntity>
{
public ChildEntity()
{
CompositeId()
.KeyProperty(x => x.Vendor, "VENDOR")
.KeyProperty(x => x.Invoice, "INVOICE")
.KeyProperty(x => x.Num, "NUM");
References(x => x.ParentEntity)
.Columns("VENDOR", "INVOICE")
.ReadOnly(); // if you are mapping the same columns of compositeId here, define it as readOnly to avoid inserting data two times on the same columns.
}
}
I did not try this code, it is just a suggestion to check what could work for you.
OK so this should be simple. I have a class
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
}
These two navigation properties are also primary keys for the table.
Declaring PRoductId and CategoryIds are redundat. How can get configure the primary keys using the nav properties?
edit: Stupid me. I forgot something very important in my question above. Those two above are to point out the config. Then we have a third fk thats the selected config for the combination of Product and category. So above entity must be a materialized entity
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
public ProductCategoryType { get; set; }
}
Declaring ProductId and CategoryId are redundant. How can get configure the primary keys using the nav properties?
Shortly - you can't. While EF6 supports shadow property based FKs, it does not provide a way to configure the PK (and many other column related settings) using the shadow property names - [Key], [Column]data annotations cannot be applied on navigation property and HasKey fluent API requires primitive property selector expression. In general EF6 does not support shadow properties in PK.
All these limitations have been removed in EF Core. But in EF6, redundant or not, you must define the actual primitive properties in the entity and map them to the composite PK.
You have only to set up a relationship between Product and Category entities by navigation properties. EF will set up the correct table structure by its own as many-to-many relationship. So no own relationship entity is needed.
Please check this out: many-to-many-relationship in EF
e.g.:
Product class:
public class Product
{
// other properties
public virtual ICollection<Category> Categories { get; set; }
}
Category class:
public class Category
{
// other properties
public virtual ICollection<Product> Products { get; set; }
}
Or did I misunderstood your question?
EDIT:
IF you need an separate entity like your ProductConfig, than you should try to set it as a unique index constraint by following:
modelBuilder
.Entity<ProductConfig>()
.HasIndex(pc => new {pc.Category, pc.Product})
.IsUnique();
For further information you should read this: HasIndex - Fluent API
EDIT 2 (after getting info solution is for EF < 6.2 needed):
Well after your last question edit, another solution approach is needed.
Here we go...
You need a structure like followed:
Product
public class Product
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
Category
public class Category
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
ProductConfig
public class ProductConfig
{
// other properties
public virtual Category { get; set; }
public virtual Product { get; set; }
public virtual ProductCategoryType { get; set; }
}
To set up a unique constraint in EF < 6.2 you have to do it like that way:
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Category)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 1) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Product)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 2) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.ProductCategoryType)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 3) { IsUnique = true }));
in EF 6.2:
modelBuilder.Entity<Person>()
.HasIndex(p => new { p.Category, p.Product, p.ProductCategoryType })
.IsUnique();
EDIT 3
If you have no primary key in your ProductConfig class or you used mine in the example where I added none, because I thought you already have that class.
It is possible to set up multiple properties as key. That will result in unique combinations too.
You would archive that with the following - instead of the index stuff:
modelBuilder.Entity<ProductConfig>()
.HasKey(pc => new { pc.Category, pc.Product, pc.ProductCategoryType });
For further information check out the MS docs.
You could also add an Id as primary key, than the indexes are needed.
I am trying to build a model with Entity Framework - Code First in which I use a "Exam" class and a "Subject" class that have a many-to-many relationship.
A "Exam" contains a list of "Subject" (Subjects).
A "Subject" contains a list of "Exam" (Exams).
Both "Exams" and "Subjects" are virtual properties.
When I use context.Exams.Include("Subjects").ToList();, I get all the exams and all the subjects related to each exam, which is OK. The problem is I also get all the exams related to the subjects.
Result :
Exam 1
Subject 1
Exam 3
Exam 4
Subject 2
Exam 3
Exam 2
...
In this particular case, I don't need the exams related to the subjects. I just need the following data :
Exam 1
Subject 1
Subject 2
Exam 2
...
Is there a way to include "Subjects" but without the "Exams" property ?
Thank you.
Function
public List<Exam> GetAllExams()
{
using (var context = new PedagogieContext())
{
return context.Exams.Include("Subjects").ToList();
}
}
Classes
public class Exam
{
public int Id { get; set; }
public string ExamLabel { get; set; }
public virtual List<Subject> Subjects { get; set; }
}
public class Subject
{
public int Id { get; set; }
public string SubjectLabel { get; set; }
public virtual List<Exam> Exams { get; set; }
}
Mappings
class SubjectMap : EntityTypeConfiguration<Subject>
{
public SubjectMap()
{
this.HasKey(e => e.Id);
this.Property(e => e.Id).HasColumnName("KeyDiscipline");
this.Property(e => e.SubjectLabel).HasColumnName("DisciplineLib");
this.ToTable("vpDisciplines");
}
}
class ExamMap : EntityTypeConfiguration<Exam>
{
public ExamMap()
{
this.HasKey(e => e.Id);
this.Property(e => e.Id).HasColumnName("KeyExamen");
this.Property(e => e.ExamenLabel).HasColumnName("ExamenLib");
this.ToTable("vExamens");
this.HasMany(e => e.Subjects)
.WithMany(d => d.Exams)
.Map(m =>
{
m.ToTable("vrExamensDisciplines");
m.MapLeftKey("KeyExamen");
m.MapRightKey("KeyDiscipline");
});
}
}
Context
public class PedagogieContext : DbContext
{
public PedagogieContext()
: base(ConnectionStringManager.GetConnectionString())
{
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Exam> Exams { get; set; }
public DbSet<Subject> Subjects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ExamMap());
modelBuilder.Configurations.Add(new SubjectMap());
}
}
The "problem" is that Entity Framework executes relationship fixup whenever you get data from the database (and on many more occasions). This is the process where EF automatically populates navigation properties (like Subject.Exams) of entities in its cache.
You are fetching exams and subjects and EF kindly populates their Subjects and Exams, respectively. There is no way to stop EF from doing this (some may think that setting Configuration.AutoDetectChangesEnabled = false will do that, but no).
Note that you don't get more exams from the database than you get from the query, if that's what you're worried about. It's just that EF also creates the associations. In a debug view you could expand the collections endlessly without ever hitting the database.
The solution is not to display Subject.Exams. If this is for serialization, you have to block circular references. Some serializers (like Json.Net) have settings to do that.
Thank you all for your answers.
Indeed, entity framework doesn't load more exams than expected. It just populates the sub-exams with the exams already loaded.
My problem was actually a circular reference serializer issue.
I chose to use DTO (with automapper) to specify exactly the data I need in my view.
http://cpratt.co/using-automapper-getting-started/