self-reference table entity framework insert record? - c#

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
ASP.NET MVC3 Entity framework 4.1
My MODEL
public class OrganizationStructure
{
public OrganizationStructure()
{
this.OrganizationStructures = new List<OrganizationStructure>();
InputDate = DateTime.Now;
}
public int ID { get; set; }
public string Name { get; set; }
public int OrganizationStructureID { get; set; }
public int OrganizationID { get; set; }
public int OrganizationTypeID { get; set; }
public int OrganizationActivityID { get; set; }
public int OrganizationLocationID { get; set; }
public string AddRemark { get; set; }
public int UserId { get; set; }
public DateTime InputDate { get; set; }
public int? RemAttr { get; set; }
public IList<OrganizationStructure> OrganizationStructures { get; private set; }
}
TABLE
ID int Unchecked
Name nvarchar(MAX) Checked
OrganizationID int Checked
OrganizationStructureID int Unchecked
OrganizationTypeID int Checked
OrganizationLocationID int Checked
OrganizationActivityID int Checked
AddRemark nvarchar(MAX) Checked
UserId int Checked
InputDate datetime Checked
RemAttr int Checked
public ICommandResult Execute(CreateOrUpdateOrganizationStructureCommand command)
{
var organizationStructure = new OrganizationStructure
{
ID = command.ID,
Name = command.Name,
OrganizationStructureID = command.OrganizationStructureID,
OrganizationID = command.OrganizationID,
OrganizationTypeID = command.OrganizationTypeID,
OrganizationActivityID = command.OrganizationActivityID,
OrganizationLocationID = command.OrganizationLocationID,
AddRemark = command.AddRemark,
UserId = command.UserId
};
if (organizationStructure.ID == 0)
_organizationStructureRepository.Add(organizationStructure);
else
_organizationStructureRepository.Update(organizationStructure);
_unitOfWork.Commit();
return new CommandResult(true);
}

The OrganizationStructureID property has to be nullable otherwise you will not be able to insert records with auto incremented primary keys.
EF will not be able to handle cyclic relationship even if you had OrganizationStructureID nullable. You need to save it in 2 steps.
using (var scope = new TransactionScope())
{
var context = new MyContext();
var organizationStructure = new OrganizationStructure {/* assign props */ };
context.OrganizationStructures.Add(organizationStructure);
context.SaveChanges(); // step 1
organizationStructure.OrganizationStructures.Add(organizationStructure);
context.SaveChanges(); // step 2
scope.Complete();
}

Related

EF Core does not insert foreign key in child table

I have the following classes:
public class PresentingComplaintModel
{
// for presenting complaints only
[Key]
public int ComplaintId { get; set; }
public string Complaint { get; set; }
public int? PatientId { get; set; }
public Patient Patient { get; set; }
public DateTime CreationDate { get; set; }
public List<PatientComplaintQuestionAnswers> PatientComplaintQuestionAnswers { get; set; }
}
This class has a one-to-many relation to this class:
public class PatientComplaintQuestionAnswers
{
[Key]
public int Id { get; set; }
public int? PresentingComplaintQuestionId { get; set; }
public PresentingComplaintQuestionModel PresentingComplaintQuestionModel { get; set; }
public bool Answer { get; set; }
public int ComplaintId { get; set; } //from PresentingComplaintModel
public PresentingComplaintModel PresentingComplaintModel {get;set;}
}
This class has two foreign keys: PresentingComplaintQuestionId and ComplaintId. The problem is when I insert a PresentingComplaintModel object into the database, the child table (PatientComplaintQuestionAnswers) receives data but the ComplaintId is always set to 0. Which means that the ComplaintId is not being populated from the primary key of the PresentingComplaintModel class.
I expect this to have non-zero primary key value. How to fix this problem?
PresentingComplaintModel is hydrated as follows. Here ComplaintId is always 0 as a new record is being created.
PresentingComplaintModel complaint = new();
List<PresentingComplaintQuestionModel> MasterQuestions { get; set; }
public PatientComplaintQuestionAnswers selectedItem1 { get; set; }
protected override async Task OnInitializedAsync()
{
complaint.PatientComplaintQuestionAnswers = new List<PatientComplaintQuestionAnswers>();
complaint.Complaint = "";
complaint.CreationDate = DateTime.Now;
complaint.PatientId = AppState.SelectedPatient.PatientId;
MasterQuestions = await DataService.GetPresentingComplaintQuestionsAsync(); //get master questions.
foreach (var q in MasterQuestions)
{
var ans = new PatientComplaintQuestionAnswers();
ans.ComplaintId = complaint.ComplaintId;
ans.PresentingComplaintQuestionId = q.PresentingComplaintQuestionId;
ans.Answer = false;
ans.PresentingComplaintQuestionModel = q;
ans.PresentingComplaintModel = complaint; //reciprocal hydrating. see https://stackoverflow.com/questions/72293081/efcore-does-not-insert-foreign-key-in-child-table/72293303#72293303
complaint.PatientComplaintQuestionAnswers.Add(ans);
}
}
And then saved to DB:
var res = await DataService.UpdatePresentingComplaintAsync(complaint);
...
...
public async Task<bool> UpdatePresentingComplaintAsync(PresentingComplaintModel complaint)
{
int res = 0;
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
try
{
context.PresentingComplaints.Update(complaint);
res = await context.SaveChangesAsync();
}
catch (Exception ex)
{
}
}
if (res > 0)
return true;
else
return false;
}
And on the database side I see these insertions. Note that the ComplaintId column has all 0s in it:
Usually this is a lack of "reciprocal" hydrating.
public class MyParent()
{
public MyParent()
{
this.TheKids = new List<MyChild>();
}
public long MyParentKey {get; set;} /* PK */
public ICollection<MyChild> TheKids {get;set;}
}
public class MyChild()
{
public long MyChildKey {get; set;} /* PK */
public long MyParentKey {get;set;} /* FK to MyParent */
public MyParent TheMyParent {get;set;} /* navigation property to MyParent */
}
When you hydrate "do not exist in the db yet" entities.. you gotta do some "reciprocal"
MyParent p1 = new MyParent();
MyChild c1 = new MyChild();
/* most people do this */
p1.TheKids.Add(c1);
/* the "reciprocal" that is sometimes missed */
c1.TheMyParent = p1;
(or it can be vice-versa, the child gets "TheMyParent" set, but the child is not added to the MyParent.TheKids collection)
Give that a try (make sure all reciprocals are set)
Note, I have NOT set MyChild.MyParentKey.
I would do it this way if the MyParent does not exist yet.
..
EF allows for a "short cut" of using FK scalars...IF they are known.
For example.
public class Employee()
{
public long EmployeeKey {get; set;} /* PK */
public string LastName {get; set;}
public string FirstName {get; set;}
public long ParentDepartmentKey {get;set;} /* FK to Department */
public Department ParentDepartment {get;set;} /* navigation property to Department */
}
If I had the above class, and when I added a new Employee..and the Employee had to have its department set to an EXISTING department.
then I could do something like
Employee emp = new Employee();
emp.LastName = "Smith";
emp.FirstName = "Mary";
emp.ParentDepartmentKey = 333; /* an already existing Department */
This way, I do NOT have to fully hydrate the emp.ParentDepartment OBJECT (navigation property) to add the Employee.
So you need to be aware of "what exist and what does not yet exist" when populating your object-graph before giving it to your EF dbcontext and persisting the changes.
Ok, I found the fix. It seems that EFCore is not setting up the foreign key column properly in this case. So I modified the PatientComplaintQuestionAnswers class with explicit ForeignKey attributes:
public class PatientComplaintQuestionAnswers
{
[Key]
public int Id { get; set; }
public int? PresentingComplaintQuestionId { get; set; }
public PresentingComplaintQuestionModel PresentingComplaintQuestionModel { get; set; }
public bool Answer { get; set; }
public int ComplaintId { get; set; } //from PresentingComplaintModel
public PresentingComplaintModel PresentingComplaintModel {get;set;}
}
To
public class PatientComplaintQuestionAnswers
{ //patient answers these questions in the patient presenting complaint form
[Key]
public int Id { get; set; }
[ForeignKey("PresentingComplaintQuestionModel")]
public int? PresentingComplaintQuestionId { get; set; }
public PresentingComplaintQuestionModel PresentingComplaintQuestionModel { get; set; }
public bool Answer { get; set; }
[ForeignKey("PresentingComplaintModel")]
public int ComplaintId { get; set; } //from PresentingComplaintModel
public PresentingComplaintModel PresentingComplaintModel {get;set;}
}
This fixed the relations and no extraneous columns were generated by EFCore either.

Data not being saved on the second loop

I am trying to input data using a loop. In the first loop it is able to succesfully input the data. In the second loop when it comes to saving the data it comes up with an error message
An exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll
but was not handled in user code:
'The property 'userTask.TaskScheduleId' is part of a key and so cannot be modified or marked as modified.
To change the principal of an existing
entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then
associate the dependent with the new principal.'
Here is the code below.
It creates a copy of the data in taskSchedule and then copies it to all the other users.
taskSchedule table has a many to many relationship with the users table which is why it is also saving data in the userTask table.
foreach(int user in users){
TaskSchedule copyTaskSchedule = new TaskSchedule();
copyTaskSchedule = taskSchedule;
copyTaskSchedule.Id = 0;
copyTaskSchedule.Notes = null;
copyTaskSchedule.UserTasks = null;
_context.TaskSchedules.Add(copyTaskSchedule);
_context.SaveChanges(); // this is where the code stops on the 2nd loop
// add updated user to the task
userTask copyUserTask = new userTask();
copyUserTask.TaskScheduleId = copyTaskSchedule.Id;
copyUserTask.UserId = user;
_context.userTasks.Add(copyUserTask);
_context.SaveChanges();
}
TaskSchedule model
public class TaskSchedule
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime? Start { get; set; }
public DateTime? End { get; set; }
public bool isClosed { get; set; }
public bool isDeleted { get; set; }
public byte priorityLevel { get; set; }
public bool hasTimeLimit { get; set; }
public Customer customer { get; set; }
public int? customerId { get; set; }
public List<Note> Notes { get; set; }
public List<AttachmentFile> Attachments { get; set; }
public List<userTask> UserTasks {get; set;}
public int? userLastEditId { get; set; }
[ForeignKey("userLastEditId")]
public User userLastEdit { get; set; }
public DateTime? userLastEditDate { get; set; }
public DateTime taskCreatedDate { get; set; }
}
looks like copyTaskSchedule.Id is a primary key or a foreign key . You are initializing the value to copyTaskSchedule.Id = 0;
I think you must change this code
copyUserTask.TaskScheduleId = copyTaskSchedule.Id;
to this
copyUserTask.TaskSchedule = copyTaskSchedule;
I removed the code below
copyTaskSchedule = taskSchedule;
Then replaced it with the following code below. I manually added each field from taskSchedule to copyTaskSchedule
copyTaskSchedule.Title = taskSchedule.Title;
copyTaskSchedule.Start = taskSchedule.Start;
copyTaskSchedule.End = taskSchedule.End;
copyTaskSchedule.userLastEditId = taskSchedule.userLastEditId;
copyTaskSchedule.priorityLevel = taskSchedule.priorityLevel;
copyTaskSchedule.isClosed = taskSchedule.isClosed;
copyTaskSchedule.hasTimeLimit = taskSchedule.hasTimeLimit;
copyTaskSchedule.Attachments = taskSchedule.Attachments;
copyTaskSchedule.customerId = taskSchedule.customerId;
copyTaskSchedule.userLastEditDate = NowDateTime;
copyTaskSchedule.isDeleted = taskSchedule.isDeleted;
copyTaskSchedule.taskCreatedDate = NowDateTime;

How feed a table with multiple foreign keys from different tables

so I have 3 models :
public class Contact
{
public int ContactID { get; set; }
public string name { get; set; }
public int SegmentID { get; set; }
public Segment Segment { get; set; }
}
public class MedicalPlan
{
public int MedicalPlanID { get; set; }
public string name { get; set; }
public int SegmentID { get; set; }
public Segment Segment { get; set; }
}
public class Target
{
public int TargetID { get; set; }
public int MedicalPlanID { get; set; }
public int ContactID { get; set; }
public MedicalPlan MedicalPlan { get; set; }
public Contact Contact { get; set; }
}
A MedicalPlan got many Contacts, and Target got both many MedicalPlans and Contacts,
Now Each MedicalPlan has a buttom called generate: Example
What I want is when you press that buttom it creates a Target and generates every Contacts that are associated to that MedicalPlan through SegmentID and insert them in the table Target as shown here
I've tried something like this :
IEnumurable<int> cons =
from contact in contacts
where contact.SegmentID == planMedical.SegmentID
select contact.ContactID;
int[] res = cons.ToArray();
for ( int j = 0; j < res.Length ; j++)
{
targets.PlanMedicalID = id; //id MedicalPlans current row's key
targets.ContactID = res[j];
_context.Add(targets);
await _context.SaveChangesAsync();
}
But it does nothing..
You are creating you ViewModel which is PlanTargets, but you have to create you Database Model Entity, you have to create the object as:
for ( int j = 0; j < res.Length ; j++)
{
var target = new Target //ADD THIS LINE
{
MedicalPlanID = id,
ContactID = res[j] ​
​ };
_context.Target.Add(target);
_context.SaveChangesAsync();
}
You are not creating a Target object, you are creating a PlanTargets object and trying to add it to your DbContext.
NOTE: In your scenerio, you want to create a Target object for every ContactID, so in your for loop you have to create the object with the new keyword, and set the related properties, after that you have to add it to your DbContext, and when you SaveChanges then it will save the results to your database.

EF6 Code First, Store update, insert, or delete statement affected an unexpected number of rows (0)

I'm having some troubles getting EntityFramework to do what I want. I have an object and I want to keep track of the current and previous state. So when there is an update the previous state will than be changed to the current state and the current state becomes a new object like this:
using (var db = new DBContext())
{
var currentPair = await db.CurrencyPairs.Include(c => c.CurrentRate).Include(c => c.PreviousRate).SingleAsync(p => p.CurrencyPairId == pair.CurrencyPairId);
var newRate = new ExchangeRate()
{
CurrencyPairId = currentPair.CurrencyPairId,
HighestBid = t.HighestBid,
LowestAsk = t.LowestAsk,
Last = t.LastPrice,
Volume = t.DailyVolume,
UpdateTime = DateTime.UtcNow
};
if (currentPair.AveragePrice == null || (DateTime.UtcNow - DateTime.Parse(db.Store.Single(s => s.Key == "CurrentStartTime").Value)).TotalHours < 4)
currentPair.AveragePrice = (t.DailyHigh + t.DailyLow) / 2;
currentPair.PreviousRate = currentPair.CurrentRate;
currentPair.CurrentRate = newRate;
await db.SaveChangesAsync();
}
The problem is that I'm getting an EntityFramework error when saving:
Store update, insert, or delete statement affected an unexpected
number of rows (0). Entities may have been modified or deleted since
entities were loaded.
The ExchangeRate object has an identity column so the ID should get filled automatically. This thread is the only one that is setting the CurrentRate/PreviousRate properties, there are other threads that read them.
Anyone has a clue as to what I'm doing wrong?
EDIT: ExchangeRate object
public class ExchangeRate
{
[Key]
public long ExhangeRateId { get; set; }
public double HighestBid { get; set; }
public double LowestAsk { get; set; }
public double Volume { get; set; }
public double Last { get; set; }
public DateTime UpdateTime { get; set; }
public long CurrencyPairId { get; set; }
}
You forget to add reference property to CurrencyPair. One foreign key id is not enough.
public class ExchangeRate
{
[Key]
public long ExhangeRateId { get; set; }
public double HighestBid { get; set; }
public double LowestAsk { get; set; }
public double Volume { get; set; }
public double Last { get; set; }
public DateTime UpdateTime { get; set; }
public long CurrencyPairId { get; set; }
public virtual CurrencyPair CurrencyPair { get; set; }
}
Here is some docs article

Entity Framework sets property to null when saving

I got a weird issue I can't explain.
I'm working with EF here. Got a method that copies some values from a wrapper object into the object I already have in the database. The object in the database has to be updated with the values in the wrapper object.
Here's the code:
private void UpdateAudit(AuditWrapper audit, DatabaseAccess dbAccess)
{
var foundAudit = dbAccess.Audits.Include("Auditors").SingleOrDefault(_ => _.Audit_ID == audit.Audit_ID);
if(foundAudit != null)
{
foundAudit.Auditorennamen = audit.Auditorennamen;
foundAudit.AuditTarget = audit.AuditTarget;
foundAudit.Scopes = audit.Scopes;
foundAudit.Location = audit.Location;
foundAudit.Address = audit.Address;
foundAudit.Auditors.Clear();
foreach (var item in audit.Auditors)
{
var usr = dbAccess.Users.SingleOrDefault(_ => _.Username == item.Username);
if (usr != null)
{
var uta = dbAccess.User_To_Audit.Create();
uta.Function = item.Function;
uta.User = usr;
uta.Audit_GeneratedID = foundAudit.GeneratedID;
uta.Audit = foundAudit;
foundAudit.Auditors.Add(uta);
}
}
}
dbAccess.SaveChanges();
}
In the User_To_Audit object the Audit is set to null, even though it wasn't null when I selected it from the database.
No idea why it is set to null when I use db.SaveChanges().
Well, I don't know if the property is really null, but I can tell it saves null into the database in the foreign key column.
I've already tried to only set the GeneratedID or only set the Audit or set none of them. Every time the same effect.
[ForeignKey("Audit")]
public int Audit_GeneratedID { get; set; }
public virtual Audit Audit { get; set; }
Please help
update:
Audit has a List<User_To_Audit>.
User_To_Audit has a reference to the Audit it belongs to.
public class Audit
{
[Key]
public int GeneratedID { get; set; }
[Index("Audit_ID", IsUnique = true)]
public int Audit_ID { get; set; }
.
.
.
public virtual List<User_To_Audit> Auditors { get; set; }
}
public class User_To_Audit
{
[Key]
public int ID { get; set; }
public virtual User User { get; set; }
[ForeignKey("Audit")]
public int Audit_GeneratedID { get; set; }
public virtual Audit Audit { get; set; }
public virtual AuditorFunction Function { get; set; }
}

Categories