I have 2 classes and the one is a collection property of the other like below.
public class NotificationMessage
{
public int ID { get; set; }
public string Message { get; set; }
public virtual ICollection<Device> Devices { get; set; }
}
public class Device
{
public int ID { get; set; }
public string Token { get; set; }
public virtual ICollection<NotificationMessage> NotificationMessages { get; set; }
}
public class NotificationMessageDevice
{
[Column(Order = 0), Key, ForeignKey("NotificationMessage")]
public int NotificationMessageID { get; set; }
[Column(Order = 1), Key, ForeignKey("Device")]
public int DeviceID { get; set; }
public virtual Device Device { get; set; }
public virtual NotificationMessage NotificationMessage { get; set; }
}
When you create a relationship like above, the entity flamework is enough clever to create many to many relationship on Database by creating NotificationMessageDevices.
The problem rises in here. Now I want to add CreateDate to the table 'NotificationMessageDevices' . What I have done is that I have created a new class called 'NotificationMessageDevice' in the way entity framework would create the table "NotificationMessageDevices" in existing database. I added new properties and called Update-Verbose. The very first exception was about missing ID property it wanted me to add public int ID {get;set;} however when I add this up it realizes the ID property is index property so that I need to remove all the data in that table.
What is the workaround of this case on Entity Framework without data loss :)
You can create the link entity like this:
public class DeviceNotificationMessages
{
public DeviceNotificationMessages()
{
CreateDate = DateTimeOffset.Now;
}
[Column(Order = 0), Key, ForeignKey("NotificationMessage")]
public int NotificationMessageId { get; set; }
public virtual NotificationMessage NotificationMessage { get; set; }
[Column(Order = 1), Key, ForeignKey("Device")]
public int DeviceID { get; set; }
public virtual Device Device { get; set; }
public DateTimeOffset CreateDate { get; set; }
}
You'd then need to handle the migration to this new entity yourself to avoid data loss. If you're using code-first migrations, you'll need to add a manual migration (automatic migration WILL result in data loss) and then edit the script so that the data isn't deleted.
Related
I need to update a child list from a parent adding records to it or updating one of its attributes. I receive the updated model from the Controller but when I try to replace the actual list with the new and save the changes to DB I get the error:
The instance of entity type 'WorkflowReferenciaExecucoes' cannot be tracked because another instance with the same key value for {'ReferenciaExecucoesId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
I don't have access to the dbContext directly because we are using the repository pattern. What I have tried to update the child in the service is:
private void Update(Workflow entity)
{
// entity is my updated model received by controller
// Getting the actual parent in the database
var workflow = GetById(entity.WorkflowId);
workflow.NomeWorkflow = entity.NomeWorkflow;
workflow.DescricaoWorkflow = entity.DescricaoWorkflow;
workflow.FgAtivo = entity.FgAtivo;
// Updating child list
workflow.WorkflowReferenciaExecucoes = entity.WorkflowReferenciaExecucoes;
// Trying to save the update gives error
_uow.WorkflowRepository.Update(entity);
}
My parent class is:
public class Workflow
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int WorkflowId { get; set; }
public int ProjetoId { get; set; }
public int WorkflowTipoId { get; set; }
public string NomeWorkflow { get; set; }
public string DescricaoWorkflow { get; set; }
public DateTime DataInclusao { get; set; }
public bool FgAtivo { get; set; }
public Projeto Projeto { get; set; }
public WorkflowTipo WorkflowTipo { get; set; }
public IEnumerable<WorkflowReferenciaExecucao> WorkflowReferenciaExecucoes { get; set; }
public IEnumerable<WorkflowCondicaoExecucao> WorkflowCondicaoExecucoes { get; set; }
}
And child class:
public class WorkflowReferenciaExecucao
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ReferenciaExecucaoId { get; set; }
public int WorkflowId { get; set; }
public int? ExecucaoWorkflowId { get; set; }
public int ValorReferenciaExecucao { get; set; }
public bool FgProcessar { get; set; }
public bool FgAtivo { get; set; }
}
What do I have to do to update the actual list to the new one?
Thank you!
Could it be that the passed in entity has duplicates in the WorkflowReferenciaExecucoes property - meaning the same WorkflowReferenciaExecucao exists twice in that IEnumerable?
you can not update like that you have wrong relationship you class should be like that
public class WorkflowReferenciaExecucao
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ReferenciaExecucaoId { get; set; }
public Workflow Workflow { get; set; }
public int? ExecucaoWorkflowId { get; set; }
public int ValorReferenciaExecucao { get; set; }
public bool FgProcessar { get; set; }
public bool Fugitive { get; set; }
}
WorkflowReferenciaExecucao is one and it has only one workflow so when you update Workflow then you have to update only workflow id in WorkflowReferenciaExecucao don't pass whole object just pass id to change it one to many relationship so on one side you update anything it don't relate to many relationship because it only point to id that it
I can reproduce your problem when there are multiple child records with the same ReferenciaExecucoesId in the update entity.
You can check if this is the case.
I am having issues trying to map two fields that are foreign keys into the same table. The use case is for a modifier and creator. My class already has the Ids, and then I wanted to add the full User object as virtual.
I am using a base class so that each of my tables have the same audit fields:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
[ForeignKey("CreatedById")]
public virtual User CreatedByUser { get; set; }
[ForeignKey("ModifiedById")]
public virtual User ModifiedByUser { get; set; }
}
The child class is very simple:
public class CircleUserSubscription : Entity
{
[Required]
public long Id { get; set; }
public long SponsorUserId { get; set; }
[ForeignKey("SponsorUserId")]
public virtual User User { get; set; }
public long TestId { get; set; }
[ForeignKey("TestId")]
public virtual User Test { get; set; }
}
This is a standard junction table.
When I try to generate the migration, I am getting errors that I don't understand fully.
Unable to determine the relationship represented by navigation property 'CircleUserSubscription.User' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I tried what this answer had, but the code is basically the same: https://entityframeworkcore.com/knowledge-base/54418186/ef-core-2-2---two-foreign-keys-to-same-table
An inverse property doesn't make sense since every table will have a reference to the user table.
For reference, here is the User entity:
public class User : Entity
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I am hoping you all can help me out, TIA :)
EDIT: One thing to note, all of this worked fine when the entity class was as follows:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
}
It was only after I added the entity that things went awry.
The Application I am trying to make is Hospital Management System. In the EnterPatientDiagnosis Form(screenshot is given at the end), I need to Add The Patient's Diagnosis Information 1st then I need to Add its associated Billing Information. Here,Both of tables Primary Key Column is an Identity Column.
This is a common step in many Systems .But I still couldn't find details on how to achieve it.
One solution I thought of is to insert all the Diagnosis Information keeping the FK_billId attribute Null using Stored Procedure and get the DiagnosisId as output parameter form the Stored Procedure. Then when user will Submit Bill information I will use the BillId and DiagnosisId to update the previously inserted row in the Diagnosis Table. But I do not like this approach for 2 reason:
Firstly, Because it has an extra update query. Since, If I used the DiagnosisId as a foreign key between this 2 Database Tables rather than BillId then there would not be any need for this update query. But I haven't found anywhere giving any rules/precedence on which Key you should use as FK in One-to-One relation.
Secondly, It conflicts with the Entity Class that I have created. I have manually created 2 classes for this 2 tables in my Entity Layer. So, If I want to insert row through entity layer then I would have to give The Billing class a new Property named DiagnosisId which is contradictory with my Database Table Schema.
Here is the 2 classes in Entity Layer:
public class EntityPatientDiagnosis
{
//Diagnosis Id is automatically assigned
public int DiagnosisId { get; set; }
public int PatientId { get; set; }
public string Symptoms { get; set; }
public string DiagnosisProvided { get; set;}
public string AdministeredBy { get; set; }
public DateTime DateofDiagnosis { get; set; }
public string FollowUpRequired { get; set; }
public DateTime DateOfFollowUp { get; set; }
public int BillId { get; set; } //BillId -> Foreign Key
}
public class EntityBilling
{
//BillId -> Primary Key ->set automatically
public int BillId { get; set; }
public int BillAmount { get; set; }
public string CardNumber { get; set; }
public string ModeOfPayment { get; set; }
}
Here is the picture of the ERD of the tables and Web Forms:
You can try a different approach. You can change the relationship between entities PatientDiagnosis and Billing. So, at first you can create a PatientDiagnosis entry and then create a Billing entry for it.
public class EntityPatientDiagnosis
{
//Diagnosis Id is automatically assigned
public int DiagnosisId { get; set; }
public int PatientId { get; set; }
public string Symptoms { get; set; }
public string DiagnosisProvided { get; set;}
public string AdministeredBy { get; set; }
public DateTime DateofDiagnosis { get; set; }
public string FollowUpRequired { get; set; }
public DateTime DateOfFollowUp { get; set; }
}
public class EntityBilling
{
//BillId -> Primary Key ->set automatically
public int BillId { get; set; }
//DiagnosisId -> Foreign Key unique
public int DiagnosisId { get; set; }
public int BillAmount { get; set; }
public string CardNumber { get; set; }
public string ModeOfPayment { get; set; }
}
I'm new to c# and I have a basic problem with saving/updating data.
With two classes :
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public DateTime RegisteredDate { get; set; }
public class Task
{
public int TaskId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public User DeclaredBy { get; set; }
}
I'm trying to save a task object (new or updated) with DeclaredBy field set
dbContext.Tasks.AddOrUpdate(task);
dbContext.SaveChanges();
User set in DeclaredBy field has an ID, but after executing SaveChanges() a new record of User appears in DB.
This is how EF works, if you add entity with other related entity then both will be stored in DB. If you want to store only one then you need to set other navigation properties to null and only set foreign key id, which currently you don't have, you should add property:
public int DeclatedById { get; set; }
Btw. this is very common problem and a lot of projects has duplicate values in db.
Maybe I should add that this will happen only if entity wasn't traced by EF context and was attached to it.
I'm wondering if this scenario is possible using Entity Framework.
I am using Code-First and have defined a Domain Model as follows:
public class PrintJob
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public virtual ICollection<StockItem> stockItemstoPrint { get; set; }
}
If I leave the above to Entity Framework and add the migration updating the database without adding the Foreign key in the StockItems Model (Which I don't want as I'd rather not have a two-way link) it will create a table for me named PrintJobStockItems which will hold PrintJobID and StockItemID
- This however is fine but I was wondering if I wanted to add a property to the PrintJobStockItems with a bool 'Printed' can it be done and have logic to update that bool value? The reason is, I want to be able to set for each individual stock item whether or not it has been printed - of course not against the stockItem Model as it should not know about PrintJobs.
If I can't achieve this, it means I will have to create a print job for every stock item, which to me isn't ideal.
You can't access the behind the scenes join table, but the workaround is to create 2 one to manys:
public class StockItem
{
public int Id { get; set; } // Identity, Key is default by convention so annotation not needed
public virtual ICollection<StockItemPrintJob> StockItemPrintJobs { get; set; }
}
public class PrintJob
{
public int Id { get; set; } // Identity, Key is default by convention
public virtual ICollection<StockItemPrintJob> StockItemPrintJobs { get; set; }
}
public class StockItemPrintJob
{
[Key, Column(Order = 0)]
public int StockItemId { get; set; }
[Key, Column(Order = 1)]
public int PrintJobId { get; set; }
public bool IsPrinted { get; set; }
public virtual StockItem StockItem{ get; set; }
public virtual PrintJob PrintJob { get; set; }}
}
Then you can do something like
var item = context.StockItemPrintJob.First(sp => sp.StockItemId == stockId && sp.PrintJobId == printJobId);
item.IsPrinted = true;
context.SaveChanges();