How to update data in a related table in EF? - c#

There are two such models:
public class Form
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid FormId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public List<BlockWorkingForm> BlocksWorkingForm { get; set; }
}
public class BlockWorkingForm
{
[Key]
[Column(Order = 1)]
public string Header { get; set; }
[Key]
[Column(Order = 2)]
public Guid FormId { get; set; }
public Form Form { get; set; }
public string Field { get; set; }
public bool MandatoryQuestion { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (!(obj is BlockWorkingForm m))
{
return false;
}
return m.Header == this.Header
&& m.Field == this.Field
&& m.Type == this.Type
&& m.MandatoryQuestion == this.MandatoryQuestion;
}
}
And there is such a method for updating the model.
public void UpdateForm(Form form)
{
EditorFormContext context = new EditorFormContext();
var formDb = this.context.Forms.Include(x => x.BlocksWorkingForm).Single(x => x.FormId == form.FormId);
this.context.Entry(formDb).CurrentValues.SetValues(form);
foreach (var itemForm in form.BlocksWorkingForm)
{
if (itemForm.FormId == Guid.Empty)
{
itemForm.FormId = formDb.FormId;
this.context.BlocksWorkingForm.Add(itemForm);
}
foreach (var itemFormDb in formDb.BlocksWorkingForm)
{
if (itemForm.Header != itemFormDb.Header)
{
continue;
}
if (!itemForm.Equals(itemFormDb))
{
this.context.Entry(itemFormDb)
.CurrentValues.SetValues(itemForm);
}
}
}
this.context.SaveChanges()
}
Now it only allows updating the Title and Description fields in the Database in the Form, as well as adding new blocks (BlockWorkingForm) for the form. But it is still necessary to implement the removal of these blocks.
To remove blocks, I need to compare what is in the database and what came in the Update method, but how can this be done?

This this.context.Entry(formDb).CurrentValues.SetValues(form); is where your properties (Title and Description) are set in the DB object. But The list of BlocksWorkingForm is not set (or not set properly).
If you add the BlocksWorkingForms in the form yourself, the insert should work properly.
This should work.
public void UpdateForm(Form form)
{
EditorFormContext context = new EditorFormContext();
var formDb = this.context.Forms.Include(x => x.BlocksWorkingForm).Single(x => x.FormId == form.FormId);
this.context.Entry(formDb).CurrentValues.SetValues(form);
formDb.BlocksWorkingForm = form.BlocksWorkingForm;
this.context.SaveChanges()
}

Related

How to update existing child class by using the parent id

Currently I have somes idea where we get the child data from its parent Id, and update the child data with hardcoded text. Parent Class:
`
public class Ride
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime RideStartTime { get; set; }
public string DestinationLongitude { get; set; }
public string DestinationLatitude { get; set; }
public int SeatAvailable { get; set; }
public Double TotalCost { get; set; } = 0;
public Double TotalDistance { get; set; } = 0;
//Ride has Many Request
public ICollection<Request> Requests { get; set; }
}
`
Child Class
public class Request : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string PickupLongitude { get; set; }
[Required]
public string PickupLatitude { get; set; }
public Double? EstimatedCost { get; set; } = 0;
public Double? Rating { get; set; } = 0;
public int RideId { get; set; }
public Ride Ride { get; set; }
}
The situation is when the when i need to update all child status column to "Confirm", i need to find it parent class first by search the rideId and if the ride found, it will update their child class attribute. Im using EF core to save the data.
// PUT api/<controller>/5
[HttpPut("{id}/confirm")]
public IActionResult ConfirmRide(int id, [FromBody]Ride ride)
{
try
{
if (ride.IsObjectNull())
{
_logger.LogError("Ride object sent from client is null.");
return BadRequest("Ride object is null");
}
if (!ModelState.IsValid)
{
_logger.LogError("Invalid ride object sent from client.");
return BadRequest("Invalid model object");
}
var dbRide = _repository.Ride.GetRideById(id);
if (dbRide == null)
{
_logger.LogError($"Ride with id: {id}, hasn't been found in db.");
return NotFound();
}
_repository.Ride.ConfirmRide(dbRide, ride, id, "Confirmed");
//_repository.Ride.
_repository.Save();
return NoContent();
}
catch (Exception ex)
{
_logger.LogError($"Something went wrong inside UpdateRide action: {ex.Message}");
return StatusCode(500, "Internal server error");
}
}
Currently this is my logic to save or update the data, can you guys help me how to update the child class base on parent Id.
How to add/update child entities when updating a parent entity in EF
I got this solution and modify it with other resource.
public void ConfirmRide(Ride dbRide, int id, string status)
{
dbRide.MapStatus(status);
Update(dbRide);
var existingParent = RepositoryContext.Rides
.Where(p => p.Id == id)
.Include(p => p.Requests).Where(r => r.Requests.Any( request => request.Status == "Approved"))
.SingleOrDefault();
if (existingParent != null)
{
foreach (var existingChild in existingParent.Requests.ToList())
{
existingChild.Status = "Confirmed";
}
}
RepositoryContext.SaveChanges();
}

Entity Framework Update Not Saving

using .net 4.5.2, MVC5, Entity framework 6 and visual studio 2015.
I have a repository pattern set up with Ninject as my DI here is the common file.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
kernel.Bind<IUserBlueRayLists>().To<UserBlueRayListRepository>().InRequestScope();
kernel.Bind<IBlueRays>().To<BlueRaysRepository>().InRequestScope();
}
my Context
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public IDbSet<UserBlueRayList> UserBlueRayLists { get; set; }
public IDbSet<BlueRays> BlueRays { get; set; }
public new void SaveChanges()
{
base.SaveChanges();
}
}
public interface IDevTestContext
{
IDbSet<UserBlueRayList> UserBlueRayLists { get; set; }
IDbSet<BlueRays> BlueRays { get; set; }
void SaveChanges();
}
Then my Repository update method.
public bool Update(UserBlueRayList item)
{
var userBRList = _db.UserBlueRayLists.FirstOrDefault(x => x.Id == item.Id);
if(userBRList != null)
{
userBRList = item;
//_db.Entry(userBRList).State = EntityState.Modified;
_db.SaveChanges();
return true;
}
return false;
}
Now when i save via my controller and call the repository update method, nothing is updated.
So i use
_db.Entry(userBRList).State = EntityState.Modified;
But i get an error,
Additional information: Attaching an entity of type 'myapp.Models.UserBlueRayList' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values... etc
Many to many models, userlist model.
public class UserBlueRayList
{
public UserBlueRayList()
{
this.BlueRays = new HashSet<BlueRays>();
}
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string UserId { get; set; }
public virtual ICollection<BlueRays> BlueRays { get; set; }
}
And the
public class BlueRays
{
public BlueRays()
{
this.UserBlueRayList = new HashSet<UserBlueRayList>();
}
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public virtual ICollection<UserBlueRayList> UserBlueRayList { get; set; }
}
Question is why this wont update, and why it errors if i try to set state to modified.
Since you are using EF6, you may try to use auto property mapping built into EF6
public bool Update(UserBlueRayList item)
{
var userBRList = _db.UserBlueRayLists.FirstOrDefault(x => x.Id == item.Id);
if(userBRList != null)
{
_dbContext.Entry(userBRList).CurrentValues.SetValues(item);
return return _dbContext.SaveChanges() > 0;
}
return false;
}
Cheers
First Solution you have to update like that
public bool Update(UserBlueRayList item)
{
var userBRList = _db.UserBlueRayLists.FirstOrDefault(x => x.Id == item.Id);
if(userBRList != null)
{
userBRList.Name = item.Name;
//value assign to other entity
_db.Entry(userBRList).State = EntityState.Modified;
_db.SaveChanges();
return true;
}
return false;
}
if this will not solve your problem you Find method instead of FirstorDefault()
public bool Update(UserBlueRayList item)
{
var userBRList = _db.UserBlueRayLists.Find(x => x.Id == item.Id);
if(userBRList != null)
{
userBRList.Name = item.Name;
//value assign to other entity
_db.Entry(userBRList).State = EntityState.Modified;
_db.SaveChanges();
return true;
}
return false;
}

Creating Object in EF which is null after saveChanges in DB

after i set the AuditReport on my Audit and save it, (in Debugger it is filled with a Proxy) there is still no Entry in the Database and I have no Idea why. Here are the relevant classes:
public class AuditReport
{
[Key]
[ForeignKey("Audit")]
[Column("AuditReport_ID")]
public int ID { get; set; }
[Required]
public virtual Audit Audit { get; set; }
}
public class Audit
{
[Key]
public int GeneratedID { get; set; }
[Index("Audit_ID", IsUnique = true)]
public int Audit_ID { get; set; }
public virtual AuditReport AuditReport { get; set; }
}
And the method in that the new AuditReport is Created
public async override Task SaveChangesAsync()
{
using (var dbAccess = new DatabaseAccess())
{
var foundAudit = dbAccess.Audits.Include("AuditReport").Include("AuditReport.Stellungnahmen").SingleOrDefault(_ => _.Audit_ID == Audit.Audit_ID);
if (foundAudit != null)
{
if (foundAudit.AuditReport == null)
{
foundAudit.AuditReport = dbAccess.AuditReports.Create();
foundAudit.AuditReport.Audit = foundAudit;
}
else
foundAudit.AuditReport.Stellungnahmen.ToList().ForEach(_ => dbAccess.Entry(_).State = EntityState.Deleted);
foreach (var item in Stellungnahmen.Where(_ => _.IsChecked == true))
foundAudit.AuditReport.Stellungnahmen.Add(dbAccess.Stellungnahmen.SingleOrDefault(_ => _.KeyWord == item.KeyWord));
}
await dbAccess.SaveChangesAsync();
}
}
As i already said, I've already debugged it and everything looks fine.
Try to remove [Key] on ID since you already have [ForeignKey] atrribute.

Compare One Model Class to Another C#

At the moment I currently have a CompanyModel that looks like so;
public class CompanyModel
{
public string CompanyID { get; set; }
public string CompanyName { get; set; }
public string CompanyAddr1 { get; set; }
public string CompanyAddr2 { get; set; }
public string CompanyTown { get; set; }
public string CompanyCounty { get; set; }
public string CompanyPcode { get; set; }
public string CompanyTelephone { get; set; }
public string CompanyAltTelephone { get; set; }
public string CompanyFax { get; set; }
public string CompanyEmail { get; set; }
public string CompanyWhoEntered { get; set; }
public DateTime CompanyDateEntered { get; set; }
public string CompanyTimeEntered { get; set; }
public string CompanyGeographicArea { get; set; }
public string CompanySearchName { get; set; }
}
What I would like to do is initialise two CompanyModels and compare the contents of both to ensure that both Companies have exactly the same data in their fields. Currently I am doing an absolutely horrendous concatenation of If statements as I am unsure of another way. This currently looks like so;
if (eCDetails.CompanyName == cCompanyDetails.CompanyName)
{
if (eCDetails.CompanyName == cCompanyDetails.CompanyName)
{
if (eCDetails.CompanyAddr1 == cCompanyDetails.CompanyName)
{
and so on and so forth (it's terrible). Is there an easier to way to ensure that both CompanyModels are equivalent?
How about using the conditional-AND (&&) operator?
if (eCDetails.CompanyName == cCompanyDetails.CompanyName &&
eCDetails.CompanyName == cCompanyDetails.CompanyName &&
eCDetails.CompanyAddr1 == cCompanyDetails.CompanyName &&
// etc...
If you have ReSharper, you can use it to auto-generate this code (and more) for you.
You are trying to write an equals method ?
You can make something like that :
if (eCDetails.CompanyName != cCompanyDetails.CompanyName)
{ return false;
}
if (eCDetails.CompanyName != cCompanyDetails.CompanyName)
{return false;
}
...
return true;
There's no many option to do what you want :)
try this .. also you have to ignore some properties
public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name)
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == to;
}
Orginal thread
Happy coding
You may also implement Equals ony youd model-class:
class CompanyModel{
public override bool Equals(object other) {
var m = (CompanyModel) other;
if (m == null) return false;
return this.CompanyName == m.CompanyName &&
this.CompanyName == m.CompanyName &&
this.CompanyAddr1 == m.CompanyAddr1 // ...
}
}
Now you can use myModelInstance.Equals(anotherInstance).

Entity Framework adding too many records

EDIT: See the bottom of this question for the working code.
I have two tables, Patients and Drugs, that I am updating with a data feed. I get a current list of patients, then iterate through and update or insert records as appropriate. This works without issue.
The trouble comes when I iterate through that patient's current medications. I end up getting multiple copies of the original patient. Drug records are transferred as expected (the records themselves don't change so new records are inserted and existing records ignored). I end up with the original patient record (inserted from UpdatePatients() below) and then one additional patient record for each medication record. Each medication record ends up with a distinct PatientId.
Class definitions:
public class Patient
{
public int PatientId { get; set; }
[Required]
public int FacilityNumber { get; set; }
[Required]
public int PatNo { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int Age { get; set; }
[Required]
public string Gender { get; set; }
[Required]
public DateTime VentStart { get; set; }
[Required]
public DateTime VentEnd { get; set; }
[Required]
public DateTime AdmitDate { get; set; }
public DateTime? DischargeDate { get; set; }
}
public class Drug
{
public int DrugId { get; set; }
[Required]
public int DrugDDI { get; set; }
[Required]
public int OrderId { get; set; }
[Required]
public string DrugName { get; set; }
[Required]
public DateTime DispenseDate { get; set; }
[Required]
public double UnitsDispensed { get; set; }
[ForeignKey("Patient")]
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
}
Offending code:
private static void UpdatePatients()
{
var Patients = DB2Patient.GetPatients();
foreach (Patient p in Patients)
{
using (var PatientContext = new VAEContext())
{
var ExistingPatientRecord = PatientContext.Patients.FirstOrDefault(
ep => ep.PatNo == p.PatNo
);
if (ExistingPatientRecord != null)
{
ExistingPatientRecord.VentEnd = p.VentEnd;
ExistingPatientRecord.DischargeDate = p.DischargeDate;
PatientContext.SaveChanges();
}
else
{
PatientContext.Patients.Add(p);
PatientContext.SaveChanges();
}
}
UpdateDrugs(p);
}
}
private static void UpdateDrugs(Patient p)
{
var Drugs = DB2Drug.GetDrugs(p.PatNo);
foreach (Drug d in Drugs)
{
using (var DrugContext = new VAEContext())
{
var ExistingDrugRecord = DrugContext.Drugs.FirstOrDefault(
ed => ed.DrugDDI == d.DrugDDI &&
ed.DispenseDate == d.DispenseDate &&
ed.OrderId == d.OrderId
);
if (ExistingDrugRecord == null)
{
d.Patient = p;
DrugContext.Drugs.Add(d);
DrugContext.SaveChanges();
}
}
}
}
EDIT: Working code:
private static void UpdatePatients()
{
var Patients = DB2Patient.GetPatients();
using (var db = new VAEContext())
{
foreach (Patient p in Patients)
{
var ExistingPatientRecord = db.Patients.FirstOrDefault(
ep => ep.PatNo == p.PatNo
);
if (ExistingPatientRecord != null)
{
ExistingPatientRecord.VentEnd = p.VentEnd;
ExistingPatientRecord.DischargeDate = p.DischargeDate;
}
else
{
db.Patients.Add(p);
}
UpdateDrugs(p, db);
}
db.SaveChanges();
}
}
private static void UpdateDrugs(Patient p, VAEContext ctx)
{
var Drugs = DB2Drug.GetDrugs(p.PatNo);
foreach (Drug d in Drugs)
{
var ExistingDrugRecord = ctx.Drugs.FirstOrDefault(
ed => ed.DrugDDI == d.DrugDDI &&
ed.DispenseDate == d.DispenseDate &&
ed.OrderId == d.OrderId
);
if (ExistingDrugRecord == null)
{
d.Patient = p;
ctx.Drugs.Add(d);
}
}
}
Why new context every time something needs to be inserted? Both methods UpdatePatients and UpdateDrugs are private, you can use the same context for all linked operations and I'm sure you won't get the duplicates:
private static void UpdateDrugs(Patient p, VAEContext context)
...
Also there's probably no need to save on every drug, doing so likely decreases performance and doesn't do much in terms of data integrity. Consider saving the context changes once per linked updates (say after UpdateDrugs is called in UpdatePatients)
Other than that you can check out the ObjectContext.Attach and related methods on how to link the Patient object to your newly created Drugs context instance
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.attach.aspx

Categories