There are two entities:
Group
Yuvak - Person
"Other" person is designed in back-end who has no group. (null)
A Yuvak - Person will always have one HomeGroup. (1=>1) And will have no groups to control.
A Nirikshak(Head) - Person will always have one HomeGroup. (1=>1) But he will also have multiple groups to control - GroupsOfNirikshak. (1=>Many)
A Group will have multiple Yuvaks (1=>Many)
and all groups mostly will have only one Head. (Initially a new group might not have any head but zero or more yuvaks-persons.)
[Table("Group")]
public class Group
{
[Key]
public int Id { get; set; }
.....
public virtual List<Yuvak> Yuvaks { get; set; }
[ForeignKey("Nirikshak")]
public int? NirikshakId { get; set; }
public virtual Yuvak Nirikshak { get; set; }
}
[Table("Yuvak")]
public class Yuvak
{
[Key]
public int Id { get; set; }
.....
[ForeignKey("HomeGroup")]
public int? HomeGroupId { get; set; }
public virtual Group HomeGroup { get; set; }
public virtual List<Group> GroupsOfNirikshak { get; set; }
}
I already has provided two foreign keys for 1=>1 relationships (nullable) in both entities.
And now to manage many to many relationship it should automatically create a third table with "Yuvak_Id" and "Group_Id" columns if they are not null. But as here the FKs are null; instead of creating a third table it adds a foreign key column in both the tables.(Group:"Yuvak_Id" and Yuvak:"Group_Id")
What should I do so that to maintain Yuvak-HomeGroup it should use above provided foreign keys only and for many to many relationship (Head-GroupsOfNirikshak & Group-Yuvaks )it should create a seperate table.
Can I create a separate table for many to many relationship like : (ID,YuvakID,GroupID) How can I do that?
I tried to do that but got different errors like below :
The navigation property 'HomeGroup' declared on type
'YTKGoshthiManagment.Models.Yuvak' has been configured with
conflicting foreign keys.
Multiplicity is not valid in Role 'Yuvak_HomeGroup_Target' in
relationship 'Yuvak_HomeGroup'. Because the Dependent Role
properties are not the key properties, the upper bound of the
multiplicity of the Dependent Role must be '*'.
.....
and so on.
Use the "Fluent Api" !
In your context class write (for example) :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>()
.HasOptional(t => t.Nirikshak)
.WithMany(t => t.GroupsOfNirikshak)
.HasForeignKey(t => t.NirikshakId);
}
You can remove the annotations on the classes and properties. Once you have a Model-based Class on another, Entity Framework will automatically create a foreign key relationship on it. It will process Yuvak as a node on the Group Graph object. You need not declare the annotations since EF will do that for you automatically.
Related
Odd issue that I've been looking at all day. I am working with Entity Framework 6. The issue I have is that I have three entities:
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
// other properties and relationships here
public int? OrderProfileId { get; set; }
public int OrderTemplateId { get; set; }
public virtual OrderProfile Profile { get; set; } // optional property
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderProfile
{
public int Id { get; set; }
// other properties
// added here 6/15/2021
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderTemplate : EntityMetaData
{
public int Id { get; set; }
// other properties
public int? OrderProfileId{ get; set; }
public OrderProfile OrderProfile { get; set; }
}
In our model builder, we have these definitions:
modelBuilder.Entity<Order>()
.HasOptional(x => x.OrderProfile)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
But even with the above fluent api model, we get the error
Invalid column name 'OrderProfile_Id'
Throughout various testing I was unable to find why this issue was occurring, so I looked at our logs and found when this error started popping it's head up and then was able to find the changes associated to OrderProfile and found that the only change that was made was adding the relationship from OrderProfile to OrderTemplate.
When I removed that fluent api relationship OrderProfile to OrderTemplate, it worked as expected... I don't need that relationship to OrderTemplate, but would like it to be there, how can I establish a optional 1 to optional 1 relationship without breaking other relationships? Also, why would additional relationships be effected by this?
UPDATE 6/15/2021
So I found I had a reverse navigation property in the OrderProfile model:
public virtual OrderTemplate OrderTemplate{ get; set; }
removing that and the associated fluent relationship
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
Doing the above resolved the issue, but for some reason, the issue seems to have cascaded down to another relationship that has a circular reference like the above. The Order class is involved with this cascaded issue. I guess this is a pretty big cause for concern since this application worked fine for the last 4 years and for these relationships to be decaying like this is worrisome. Does anyone know why this is happening?
if you use the right naming convention, EF will do magic. in this sample, you don't need fluent API to relate entities.
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
public int? OrderProfileId { get; set; } //means HasOptional (nullable) and ForeignKey
//variable name must be OrderProfile not Profile
public virtual OrderProfile OrderProfile { get; set; }
}
public class OrderProfile
{
public OrderProfile()
{
Orders = new HashSet<Order>();
}
public int Id { get; set; }
//be aware circular reference at any conversion or mapping
public virtual ICollection<Order> Orders {get; set;} //means WithMany
}
I've got an error like this too. It's caused by unmatching OrderProfileId property in OrderTemplate class with the fluent api model
If I'm not wrong, you want the OrderProfile model a many to many relation between Order and OrderTemplate. Then if it was the case, add the nvaigation property in OrderProfile.
public class OrderProfile
{
public int Id { get; set; }
// other properties
public virtual ICollection<Order> Orders { get; set; }
public virtual OrderTemplate OrderTemplate { get; set; }
}
Then change the fluent api model to be like this
// the EF has modelled the relation for normal 1 to many relation
// modelBuilder.Entity<Order>()
// .HasOptional(x => x.OrderProfile)
// .WithMany(x => x.Orders)
// .HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderTemplate>()
.HasOptional(x => x.OrderProfile)
.WithOptional(x => x.OrderTemplate);
You're working database-first, which always leaves room for a mismatch between the actual database model and the model EF infers from class and property names and mapping code (= conceptual model). If this happens, it may help to make EF generate a database from the conceptual model and see where it creates the column it expects, OrderProfile_Id.
This is what you'll see when logging the SQL statements:
CREATE TABLE [dbo].[OrderTemplates] (
[Id] [int] NOT NULL IDENTITY,
[OrderProfileId] [int],
[OrderProfile_Id] [int],
CONSTRAINT [PK_dbo.OrderTemplates] PRIMARY KEY ([Id])
)
...
ALTER TABLE [dbo].[OrderTemplates]
ADD CONSTRAINT [FK_dbo.OrderTemplates_dbo.OrderProfiles_OrderProfile_Id]
FOREIGN KEY ([OrderProfile_Id]) REFERENCES [dbo].[OrderProfiles] ([Id])
There you see the expected nullable column OrderProfile_Id which is the FK to OrderProfiles. It's noteworthy to see that EF does not use OrderProfileId as a foreign key field. It's just a field that could be used for anything.
That's because EF6 doesn't support 1:1 associations as foreign key associations (reference property and primitive FK property).
Knowing this, the remedy is simple: remove the property OrderTemplate.OrderProfileId and tell EF to use the field OrderTemplate.OrderProfileId in the database:
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile)
.Map(m => m.MapKey("OrderProfileId"));
That said, I wonder why Order has a foreign key to OrderProfile. Isn't its OrderProfile determined by its OrderTemplate? If it's a redundant relationship it may be better to remove it.
I am developing a simple web application where a doctor is adding multiple prescription records for patients and will select multiple drugs while doing prescription. So one patient has multiple prescription and one prescription has multiple selected drugs. I have taken one another table patientrecords for reporting purpose/Normalization perspective where I am referencing patientID and PrescriptionID.
One patient --> many prescriptions --> one to many relationship
One prescriptions -> many drugs --> one to many relationship
Below is the model for patient, prescription and drugs, PatientRecord table.
While running migration, I get this error:
Error Number:1769,State:1,Class:16
Foreign key 'FK_Drugs_Prescriptions_PrescriptionID' references invalid column 'PrescriptionID' in referencing table 'Drugs'.
I am confused with explanation of one to many relationships on Microsoft website.
Can anyone help me with it?
There are two ways to configure the relationships in EF Core
Conventions :By default, a relationship will be created when there is a navigation property discovered on a type. Not applicable to many-to-many relationship
Fluent API:you start by identifying the navigation properties that make up the relationship. HasOne or HasMany identifies the navigation property on the entity type you are beginning the configuration on. HasOne/WithOne are used for reference navigation properties and HasMany/WithMany are used for collection navigation properties.
From your screenshots and the benjamin suggested, you could configure the model like below
Patient - Prescription --> one to many relationship
Prescription - Drug --> many to many relationship
public class Prescription
{
public int PrescriptionId { get; set; }
[Required]
public string Description { get; set; }
[Required]
public DateTime PrescriptionDate { get; set; }
public int PatientId { get; set; }
public Patient Patient { get; set; }
public ICollection<DrugPrescription> DrugPrescriptions { get; set; }
}
public class Drug
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int CurrentStock { get; set; }
public int DrugCost { get; set; }
public string Description { get; set; }
public ICollection<DrugPrescription> DrugPrescriptions { get; set; }
}
//represent a many-to-many relationship by including an entity class for
//the join table and mapping two separate one-to-many relationships.
public class DrugPrescription
{
public int DrugId { get; set; }
public Drug Drug { get; set; }
public int PrescriptionId { get; set; }
public Prescription Prescription { get; set; }
}
//DbContext
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{}
public DbSet<Patient> Patient { get;set; }
public DbSet<Drug> Drug { get;set; }
public DbSet<Prescription> Prescription { get;set; }
public DbSet<PatientRecord> PatientRecord { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
#region Drug-Prescription Many-to-Many
builder.Entity<DrugPrescription>()
.HasKey(dp => new { dp.DrugId, dp.PrescriptionId });
builder.Entity<DrugPrescription>()
.HasOne(dp => dp.Prescription)
.WithMany(p => p.DrugPrescriptions)
.HasForeignKey(dp => dp.PrescriptionId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<DrugPrescription>()
.HasOne(dp => dp.Drug)
.WithMany(d => d.DrugPrescriptions)
.HasForeignKey(dp => dp.DrugId)
.OnDelete(DeleteBehavior.Restrict);
#endregion
}
}
There are a few things that don't quite look right here. Maybe if you clean them up you'll be close to spotting where the error is.
Firstly, I'm a bit confused by your PatientRecord class. It identifies itself with a PatientRecordId and it maps to a Patient, but it doesn't add any other information, so what is it for? If you're not going to add anything to that class, I think you can remove it from the model.
Secondly, your Prescription class maps to a collection of Drugs. That's perfect because you have a one-to-many relationship between them... so why does it also have an integer DrugId property? Unless you want the Prescription class to reference the Id of one single Drug as well as the collection of Drugs, I think you should remove it. It might be confusing Entity Framework and not giving you any value.
Thirdly, your Drug class maps to one Prescription (through its properties Prescription and PrescriptionId) but why? Presumably a drug can appear on multiple prescriptions, as it could be prescribed to many people, or prescribed to the same person several times. So I think you want to remove that too and replace it with a many-to-many relationship.
Finally, if you want to have a many-to-many relationship between Prescription and Drug (and I think you will) you probably need to add a DrugPrescription class, with a Drug property and a Prescription property, to create this many-to-many mapping.
I think if you do that you'll be a lot close to your goal, and your error message will probably go away.
In EF6 we have two ways to declare relationship between two tables:
Annotations Attributes
Fluent API
Today (by accident) I removed one relationship between two tables and everything kept working well. I was very surprised because there was no way EF would know how those two tables are connected.
Tables look like that:
[Table("item")]
public class Item
{
public Item()
{
ItemNotes = new HashSet<ItemNote>();
}
[Key]
[Column("itemID", TypeName = "int")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int itemID { get; set; }
public ICollection<ItemNote> ItemNotes { get; set; }
}
[Table("itemNotes")]
public class ItemNote
{
[Key]
[Column("id", TypeName = "int")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("itemID", TypeName = "int")]
public int ItemId { get; set; }
[Column("note", TypeName = "varchar")]
[MaxLength(255)]
public string Note { get; set; }
}
Fluent API:
public class MyContext : DbContext
{
public MyContext()
: base("name=MyContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<MyContext>(null);
//I removed this relationship:
//modelBuilder.Entity<Item>().HasMany(i => i.ItemNotes).WithRequired().HasForeignKey(i => i.ItemId);
base.OnModelCreating(modelBuilder);
}
}
Here is the test I made: It's an integration test, that connects to the real database, gets items with notes and tests the EF:
[TestCase]
public void QueryItemWithItemNotesTest()
{
FniContext fniContext = new FniContext();
int itemId = fniContext.Database.SqlQuery<int>("SELECT TOP(1) itemId FROM item WHERE itemId IN (SELECT itemId FROM dbo.itemNotes)").FirstOrDefault();
var item = fniContext.Items.AsNoTracking()
.Include(i => i.ItemNotes)
.Where(i => i.itemID == itemId).FirstOrDefault();
Assert.IsNotNull(item);
Assert.Greater(item.ItemNotes.Count, 0);
}
It passes! It loads all notes! How come?!
I kept investigating and it turned out that in case of 1:many relationship I totally don't have to have any relationship in the code. The only time I need it is with 1:1 relationship. Am I'm missing something? Most of relationships are 1:many, so does it mean Fluent API is used for 1:1 most of the time?
Entity Framework has some conventions that you do not need to define explicitly.
From https://msdn.microsoft.com/en-us/library/jj679962(v=vs.113).aspx#Anchor_2
In addition to navigation properties, we recommend that you include
foreign key properties on the types that represent dependent objects.
Any property with the same data type as the principal primary key
property and with a name that follows one of the following formats
represents a foreign key for the relationship: '<navigation property
name><principal primary key property name>', '<principal class
name><primary key property name>', or '<principal primary key property
name>'. If multiple matches are found then precedence is given in the
order listed above. Foreign key detection is not case sensitive. When
a foreign key property is detected, Code First infers the multiplicity
of the relationship based on the nullability of the foreign key. If
the property is nullable then the relationship is registered as
optional; otherwise the relationship is registered as required.
I have a simple user table:
CREATE TABLE User
(
UserId int,
UserName nvarchar(35),
Password nvarchar(size),
);
I want to split this up into two entities in EF6. User and UserPassword. These represent two very different business needs, but happen to live on the same table.
So I create two entities.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
}
public class UserPassword
{
public int Id { get; set; }
public string Password{ get; set; }
}
I have maps like so
class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
Property(p => p.Id).HasColumnName("UserId");
}
}
class PasswordMap : EntityTypeConfiguration<UserPassword>
{
public PasswordMap()
{
ToTable("User");
Property(p => p.Id).HasColumnName("UserId");
}
}
But when I use these entities I get the following error:
Message = "The entity types 'UserPassword' and 'User' cannot share table 'User' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them."
I do not want to create a relationship. I am having trouble setting up table per hierarchy. I don't want a discriminator. I really just want to have two separate ways to get to the same table that are discreet. Is there a way to do this other than creating two contexts?
Ah. As per my comment, if you created an updatable view for UserPassword then you could use that as the table for the entity. EF will neither know nor care that updating that is actually updating the users table, and vice versa.
I have 3 tables where Property has a foreign key to Dependent and Dependent has a foreign key to Main, causing a One-to-Many relationship for each of the tables. However I am only interested in the most recent record in Dependent and its Property records, thus i created a view v_Dependent which returns the most recent Dependant record grouped by MainId. This will enable a One-to-One relationship between Main and Dependant which is what I'm after, works with the code below.
I am eager loading all when loading Main objects, however after i switched to the view I may no longer eager load the records in the Properties collection of Dependent. The reason for this is that to map the view into a One-to-One relationship I had to add MainId to the composite key for Dependent. Now the foreign key from Property would have to contain the MainId as well to be able to load the collection, however I do not have MainId in the database table, nor do I want to.
My question is, do I have to create a view for Property as well to include the MainId and add this to the entity composite foreign key, or is there anything else I can do to map this using fluent API? Another option I'm currently using is explicitly loading the Property collection in my repository, however I was hoping fluent API could handle this for me. The commented out line is the config which worked for the whole graph while I treated the Dependent as a collection on Main. I am using the entities read only, so dont have to worry about storing back.
public class Main
{
public int MainId { get; set; }
public Dependent Dependent { get; set; }
}
public class Dependent
{
public int DependentId { get; set; }
public int MainId { get; set; }
public Main Main { get; set; }
public ICollection<Property> Properties { get; set; }
}
public class Property
{
public int PropertyId { get; set; }
public int DependentId { get; set; }
public Dependent Dependent { get; set; }
}
public class SomeContext : DbContext
{
public DbSet<Main> Mains { get; set; }
public DbSet<Dependent> Dependents { get; set; }
public DbSet<Property> Properties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Main>().ToTable("Main").HasKey(m => m.MainId);
//modelBuilder.Entity<Dependent>().ToTable("Dependent").HasKey(d => d.DependentId).HasRequired(d => d.Main).WithMany(m => m.Dependents).HasForeignKey(d => d.MainId);
modelBuilder.Entity<Dependent>().ToTable("v_Dependent").HasKey(d => new {d.DependentId, d.MainId}).HasRequired(d => d.Main).WithOptional(m => m.Dependent);
modelBuilder.Entity<Property>().ToTable("Property").HasKey(p => p.PropertyId).HasRequired(p => p.Dependent).WithMany(d => d.Properties).HasForeignKey(p => p.DependentId);
base.OnModelCreating(modelBuilder);
}
}
After alot of digging I found out that the underlying issue is that entity framework is unable to map a One to Zero or One relationship using a foreign key instead of primary key. See https://entityframework.codeplex.com/workitem/299
As a result I had to choose between changing the primary key of Dependent (which is currently a view and thus I could create an indexed view with all the limitations of such, but I have a subquery I cannot get rid of) or load the properties seperately. I chose the latter and changed the applcation to no longer have a navigation property and loading the properties when needed.