EF: Multiple 1-to-1 or zero on same parent table - c#

I have a problem similar to Entity Framework Code First : Setting up One-To-One foreign key association using Annotations
However, I have many "optional" references on the same parent table to same child table.
If I call classes CE and CA, to simplify, CE has a number of CA fields. CA id's, then, cannot have as key only the same id as CE.
What I want is EF to recognize CA/id as primary key and ConfiguracionEmpresaId as the foreign key relationship. Simply adding it to the keys didn't work.
I tried many things and I think is time to ask for help.
Simplified code:
public class ConfiguracionEmpresa
{
[Key]
[ForeignKey("Empresa")]
public long Id { get; set; }
public virtual Empresa Empresa { get; set; }
[StringLength(200)]
public string DirectorioTrabajo { get; set; }
public virtual ConfiguracionArchivo Cfg_LibroMayor { get; set; }
public virtual ConfiguracionArchivo Cfg_LibroDiario { get; set; }
public virtual ConfiguracionArchivo Cfg_Balance { get; set; }
public virtual ConfiguracionArchivo Cfg_DiccionarioCuentas { get; set; }
}
public class ConfiguracionArchivo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long ConfiguracionEmpresaId { get; set; }
[Required, ForeignKey("ConfiguracionEmpresaId")]
public virtual ConfiguracionEmpresa ConfiguracionEmpresa { get; set; }
public int LineasCabecera { get; set; }
}
The problem is I cannot have the same parent ID on child table, it will have duplicated primary keys.
On ModelBuilder:
modelBuilder.Entity<ConfiguracionArchivo>().HasKey(e => e.Id).HasRequired(e => e.ConfiguracionEmpresa);
modelBuilder.Entity<ConfiguracionEmpresa>()
.HasOptional(d => d.Cfg_Balance).WithRequired(q => q.ConfiguracionEmpresa);
modelBuilder.Entity<ConfiguracionEmpresa>()
.HasOptional(d => d.Cfg_DiccionarioCuentas).WithRequired(q => q.ConfiguracionEmpresa);
modelBuilder.Entity<ConfiguracionEmpresa>()
.HasOptional(d => d.Cfg_LibroDiario).WithRequired(q => q.ConfiguracionEmpresa);
modelBuilder.Entity<ConfiguracionEmpresa>()
.HasOptional(d => d.Cfg_LibroMayor).WithRequired(q => q.ConfiguracionEmpresa);

Related

N:N Entity Framework C# Relationship

I'm having a problem with many-to-many relationships in a pizzeria system I'm developing.
I have an entity called "Payament" that has a list of Pizzas and a list of Drinks.
ex:
public class Payament
{
public string? PayamentId { get; set; }
public virtual List<Pizza> Pizzas { get; set; }
public virtual List<Drink> Drinks { get; set; }
public string? CPFId { get; set; }
[JsonIgnore]
public virtual Client? Client { get; set; }
public double? TotalPay { get; set; }
public DateTime DateTransaction { get; set; }
public virtual StatusOrder StatusOrder { get; set; }
public Payament()
{
Pizzas = new List<Pizza>();
Drinks = new List<Drink>();
DateTransaction = DateTime.Now;
StatusOrder = StatusOrder.CARRINHO;
}
}
Also, I have two entities Pizza and Drink.
ex:
public class Pizza : IItem
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public double Value { get; set; }
[JsonIgnore]
public virtual List<Payament> Payament { get; set; }
public Pizza()
{
Payament = new List<Payament>();
}
}
public class Drink : IItem
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public double Value { get; set; }
[JsonIgnore]
public virtual List<Payament> Payament { get; set; }
public Drink()
{
Payament = new List<Payament>();
}
}
These classes are configured as many-to-many in OnModelCreating()...
Drink:
builder.HasMany(h => h.Payament)
.WithMany(d => d.Drinks);
Pizza:
builder.HasMany(h => h.Payament)
.WithMany(p => p.Pizzas);
Payament:
builder.HasMany(p => p.Pizzas)
.WithMany(p => p.Payament);
builder.HasMany(d => d.Drinks)
.WithMany(d => d.Payament);
Migrations were generated, all right. But when I go to send a payment, I get this error:
MySqlException: Cannot add or update a child row: a foreign key constraint fails (pizzaroller.drinkpayament, CONSTRAINT FK_DrinkPayament_Payament_PayamentId FOREIGN KEY (PayamentId) REFERENCES payament (PayamentId) ON DELETE CASCADE)
What I believe it could be:
If I'm not mistaken, a drinkpayament table and a pizzapayament table are created where the primary keys between the relations are joined.
And in this case, "Payament" has a primary key of type string, while items "Pizza" and "Drink" have a primary key of type int.
The conflict is likely to be found in this divergence. I could be wrong, of course.
So I would like to know how I can solve this problem. And also, if possible, tips on how I can work with existing items for sale and relate them to a payment.
Project is on github if you want to take a look:
https://github.com/newhobbye/pizza-roller-api
This project is for the exercise of knowledge and some patterns that I will implement to practice. I accept any kind of criticism! A big hug and many thanks!
I managed to solve.
I was resending the client entity in update. And I hadn't turned off the AsNoTracking() option;
In addition to this problem, I was also resending the items instead of associating the existing ones by id.
Thank you all for your help!

.NET Core Entity Framework many to many relation same entity (related products)

I'm trying to built a related items type model using Entity Framework in my .NET Core 1.1 MVC application. I keep running into the following error (tried all combinations of deletebehaviours with foreign keys):
Introducing FOREIGN KEY constraint
'FK_MenuItemRelation_MenuItems_RelatedMenuItemId' on table
'MenuItemRelation' 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 or index. See previous
errors.
Before I go and simply use a mapping table, I would like to hear the community input on this.
Model builder
//MenuItem <> MenuItem many to many (related item) mapping
modelBuilder.Entity<MenuItemRelation>()
.HasKey(mr => new { mr.PrimaryMenuItemId, mr.RelatedMenuItemId });
modelBuilder.Entity<MenuItemRelation>()
.HasOne(mr => mr.PrimaryMenuItem)
.WithMany()
.HasForeignKey(mr => mr.PrimaryMenuItemId);
modelBuilder.Entity<MenuItemRelation>()
.HasOne(mr => mr.RelatedMenuItem)
.WithMany()
.HasForeignKey(mr => mr.RelatedMenuItemId).OnDelete(DeleteBehavior.Restrict);
Domain model
public class MenuItem
{
public string Category { get; set; }
public string Description { get; set; }
public long ID { get; set; }
public Menu Menu { get; set; }
public string Name { get; set; }
public string PictureUrls { get; set; }
public float Price { get; set; }
public string Reference { get; set; }
public ICollection<MenuItemRelation> RelatedItems { get; set; }
public string Status { get; set; }
}
Mapping Entity
public class MenuItemRelation
{
public MenuItem PrimaryMenuItem { get; set; }
public long PrimaryMenuItemId { get; set; }
public MenuItem RelatedMenuItem { get; set; }
public long RelatedMenuItemId { get; set; }
}
So I modeled the desired SQL outcome and reverse engineered the many to many relationship by using database first approach as explained here Entity Framework Core creating model from existing database. The result is below.
Basically it just required me to add both the incoming relation as well as the outgoing relation on the menuitem class.
Feel free to comment if there are better ways of doing this.
Table TSQL
Create TABLE MenuItem(
ID int IDENTITY(1,1),
Name nvarchar(max)
PRIMARY KEY (ID)
)
GO
CREATE TABLE MenuItemRelation (
PrimaryMenuItemId int,
RelatedMenuItemId int,
PRIMARY KEY (PrimaryMenuItemId, RelatedMenuItemId),
FOREIGN KEY (PrimaryMenuItemId) REFERENCES MenuItem (ID),
FOREIGN KEY (RelatedMenuItemId) REFERENCES MenuItem (ID)
)
GO
Insert into dbo.MenuItem values ('MenuItemA')
Insert into dbo.MenuItem values ('MenuItemB')
Insert into dbo.MenuItem values ('MenuItemC')
GO
INSERT into dbo.MenuItemRelation values (1,2)
INSERT into dbo.MenuItemRelation values (1,3)
GO
DELETE from dbo.MenuItem where ID = 1
GO
//Confirmed no cascading happens
Domain model (with some naming edits)
public class MenuItem
{
public string Category { get; set; }
public string Description { get; set; }
public long ID { get; set; }
public Menu Menu { get; set; }
public string Name { get; set; }
public string PictureUrls { get; set; }
public float Price { get; set; }
public string Reference { get; set; }
public ICollection<MenuItemRelation> RelatedItems { get; set; }
public ICollection<MenuItemRelation> RelatedTo { get; set; }
public string Status { get; set; }
}
Mapping object
public class MenuItemRelation
{
public MenuItem PrimaryMenuItem { get; set; }
public long PrimaryMenuItemId { get; set; }
public MenuItem RelatedMenuItem { get; set; }
public long RelatedMenuItemId { get; set; }
}
Model builder
modelBuilder.Entity<MenuItemRelation>()
.HasKey(e => new { e.PrimaryMenuItemId, e.RelatedMenuItemId });
modelBuilder.Entity<MenuItemRelation>()
.HasOne(d => d.PrimaryMenuItem)
.WithMany(p => p.RelatedItems)
.HasForeignKey(d => d.PrimaryMenuItemId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<MenuItemRelation>()
.HasOne(d => d.RelatedMenuItem)
.WithMany(p => p.RelatedTo)
.HasForeignKey(d => d.RelatedMenuItemId)
.OnDelete(DeleteBehavior.Restrict);

Setup One to Many without Foreign Key Entity Framework

I have the following design.
As we can see from this image, an Episode will have multiple EpisodePatients, and each of these EpisodePatients, will point to one Episode.
Here are my two models corresponding to the tables.
public class EpisodeModel
{
public int ID { get; set; }
public virtual EpisodePatientModel EpisodePatient { get; set; }
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public virtual EpisodeModel Episode { get; set; }
}
How do I setup the One to Many relationship between EpisodeModel and EpisodePatientModel?
Since EpisodeModel does not contain a foreign key to EpisodePatient, I cannot do the following.
modelBuilder.Entity<EpisodeModel>().HasRequired(r => r.EpisodePatient).WithMany().HasForeignKey() //No foreign key
I have tried this.
modelBuilder.Entity<EpisodeModel>().HasRequired(r => r.EpisodePatient);
But with this approach, the EpisodeModel is not Lazy loaded when loading all EpisodePatientModels form the DB
First of all your model does not reflect what you say. If there is one-to-many relationship between EpisodeModel and EpisodePatientModel you must have collection of EpisodePatientModel. And you are missing foreign key property at EpisodePatientModel:
public class EpisodeModel
{
public int ID { get; set; }
public virtual ICollection<EpisodePatientModel> EpisodePatients { get; set; } // Must be collection
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public int EpisodeID { get; set; } // Foreign key to Episode
public virtual EpisodeModel Episode { get; set; }
}
Then after your models are correct, mappings with Fluent API is easy to understand, map them as you say: one EpisodeModel can have many EpisodePatientModel:
modelBuilder.Entity<EpisodeModel>()
.HasMany(r => r.EpisodePatients)
.WithRequired(m => m.Episode)
.HasForeignKey(m => m.EpisodeID);
Or you can map reverse of this. Adding one of these two is enough:
modelBuilder.Entity<EpisodePatientModel>()
.HasRequired(r => r.Episode)
.WithMany(m => m.Episode)
.HasForeignKey(m => m.EpisodeID);
I understood that you need to have a foreign key in the EpisodePatient table that refers to the Episode table Id, so I think you can solve it like this:
public class EpisodeModel
{
public int ID { get; set; }
public virtual IEnumerable<EpisodePatientModel> EpisodePatients { get; set; }
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public int EpisodeID { get; set; }
public virtual EpisodeModel Episode { get; set; }
}
And then, your configuration should go like this:
modelBuilder.Entity<EpisodePatientModel>().HasRequired(r => r.Episode).WithMany(e => e.EpisodePatients).HasForeignKey(r => r.EpisodeID);

EF Code First - mapping two foreign key columns in child table to the same primary key

Say you have a table of road mile-marker points (mile-markers are signs placed every mile on US highways). Then you have a second table of spans between these mile markers. The Span table has two int columns StartMileMarkerId and EndMileMarkerId which are foreign keys referencing MileMarker Id column; These are the tables;
tblMileMarkers
[Table("tblMileMarkers")]
public class MileMarker
{
public MileMarker()
{
Spans = new HashSet<Span>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DbGeography Location { get; set; }
public virtual ICollection<Span> Spans { get; set; }
}
tblSpans
[Table("tblSpans")]
public class Span
{
[Key]
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
public int StartMileMarkerId { get; set; }
public int EndMileMarkerId { get; set; }
public virtual MileMarker MileMarker { get; set; }
}
If it was only one foreign key (StartMileMarkerId), I could configure the one-many relationship in the DbContext with Fluent Api as below
modelBuilder.Entity<Span>().HasRequired(s => s.MileMarker)
.WithMany(m => m.Spans)
.HasForeignKey(s => s.StartMileMarkerId);
How can I map these 2 columns (StartMileMarkerId and EndMileMarkerId) to the same primary key?
Since you have 2 foreign keys, you need 2 collection navigation properties in MileMarker and 2 reference navigation properties in Span. Something like this:
MileMarker class:
public virtual ICollection<Span> StartSpans { get; set; }
public virtual ICollection<Span> EndSpans { get; set; }
Span class:
public virtual MileMarker StartMileMarker { get; set; }
public virtual MileMarker EndMileMarker { get; set; }
Configuration:
modelBuilder.Entity<Span>()
.HasRequired(s => s.StartMileMarker)
.WithMany(m => m.StartSpans)
.HasForeignKey(s => s.StartMileMarkerId);
modelBuilder.Entity<Span>()
.HasRequired(s => s.EndMileMarker)
.WithMany(m => m.EndSpans)
.HasForeignKey(s => s.EndMileMarkerId);
P.S. If your idea is to have ModelMarker.Spans collection mapped to spans with either start or end marker being this marker, it's just not possible.

Related many to one navigation properties not loading with EF code first

I am struggling to get some related navigation properties loading up using EF code first.
I have a User table which is my primary table, which every time you update it, it generates a UserLight object containing only the basics. This is linked via a one-to-one mapping with User, so the User object generates the identity key and then when it saves, a UserLlight object is created using that UserId as it's key.
I now have a conversation object between two users which I want to load in only the UserLight objects for the sender and receiver, for performance reasons. I have tried mapping using Fluent and CF but when I load the objects from my repository, only the UserStartedId and UserRecipientId integer fields are populated, the actual UserLight objects UserStarted and UserRecipient are null.
My conversation class is as follows
public class DbConversation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long ConversationId { get; set; }
public virtual DbUserLight UserStarted { get; set; }
public int UserStartedId { get; set; }
public virtual DbUserLight UserRecipient { get; set; }
public int UserRecipientId { get; set; }
public virtual IList<DbStoryMessage> Messages { get; set; }
}
My Userlight class is as follows (abbreviated)
public class DbUserLight
{
public int UserId { get; set; }
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
public DateTime DateOfBirth { get; set; }
}
My DbContext OnModelCreating has the following
modelBuilder.Entity<DbUserLight>()
.HasKey(a => a.UserId);
modelBuilder.Entity<DbUserLight>()
.Property(a => a.UserId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<DbStoryConversation>()
.HasKey(c => c.ConversationId);
modelBuilder.Entity<DbStoryConversation>()
.Property(c => c.ConversationId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<DbStoryConversation>()
.HasMany<DbStoryMessage>(c => c.Messages)
.WithRequired(m => m.Conversation)
.WillCascadeOnDelete(true);
And my repository call is as follows
public IQueryable<DbStoryInboxMessage> GetInboxMessages()
{
return Work.Context.StoryInboxMessages
.Include(i => i.Conversation.UserStarted)
.Include(i => i.Conversation.UserRecipient)
.Include(i => i.Conversation.Messages);
}
Can anyone shed any light on why this is not working?
Did you try adding the foreign key annotations?
public class DbConversation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long ConversationId { get; set; }
[ForeignKey("UserStartedId")]
public virtual DbUserLight UserStarted { get; set; }
public int UserStartedId { get; set; }
[ForeignKey("UserRecipientId")]
public virtual DbUserLight UserRecipient { get; set; }
public int UserRecipientId { get; set; }
public virtual IList<DbStoryMessage> Messages { get; set; }
}

Categories