Sam
Sam

Reputation: 5637

Entity Framework not saving update for just one property on just one Entity Type

I am using ASP.Net Core MVC and Entity Framework Core in Visual Studio 2017.

We are using a pretty straight forward Repository Pattern.

I'm eight months into this project and this is the first time I am having this strange problem on this one property on this one entity getting saved to the Database on SaveChanges after UpdateChanges.

So here is the flow.

1) I have a Create controller action to save a new entity called Recommendation. The Recommendation has a parent entity called finding. When I create a new recommendation I have to update the status on the parent finding. Also the Finding entity has a parent Entity called Audit. When I edit the finding status I also have to update the audit status.

Here is some code for this.

[HttpPost]
public IActionResult Create(CreateIntRecommendationVM createIntRecommendationVM)
{
    int findingId = createIntRecommendationVM.Finding.FindingId;
        Finding finding = _findingRepo.Findings
            .Include(f => f.Audit)
            .Where(f => f.FindingId == findingId)
            .FirstOrDefault();

    if (RecModelStateIsValid(ModelState))
        {
            ClaimsPrincipal user = HttpContext.Request.HttpContext.User;
            short staffId = short.Parse(user.Claims.Single(c => c.Type == "StaffId").Value);

            Recommendation recommendation = createIntRecommendationVM.Recommendation;
            recommendation.RecFindingId = findingId;

            #region Get Recommendation Number
            recommendation.RecCd = GetRecommendationNumber(findingId);
            #endregion

            recommendation.RecStatusId = 10;
            recommendation.RecStaffId = staffId;
            recommendation.RecLastUpdateDt = DateTime.Now;

            _recommendationRepo.Add(recommendation);
            _recommendationRepo.SaveChanges();

            bool unresolvedFinding = false;
            bool unresolvedAudit = false;
            int? oldFindingStatus = finding.FindingStatusId;
            if (finding.FindingStatusId != 10)
            {
                finding.FindingStatusId = 10;
                unresolvedFinding = true;
            }
            if (oldFindingStatus != 10 && finding.Audit.StatusID != 10)
            {
                finding.Audit.StatusID = 10;
                unresolvedAudit = true;
            }

            _findingRepo.Update(finding);
            _findingRepo.SaveChanges();

When I run in debug mode and put a break point and inspect while I am stepping through, I am definately setting finding.FindingStatusId to 10 and finding.Audit.StatusID to 10.

_findingRepo.Update(finding);

hits this repo:

public class FindingRepository : IFindingRepository
    {
        private ApplicationDbContext context;
        public FindingRepository(ApplicationDbContext ctx)
        {
            context = ctx;
        }

        public IQueryable<Finding> Findings => context.Findings;

        public Finding Get(int id)
        {
            Finding finding = context.Findings.Find(id);
            return finding;
        }

        public void Add(Finding finding)
        {
            context.Findings.Add(finding);
        }

        public void Update(Finding finding)
        {
            context.Findings.Update(finding);
        }

        public void Delete(int id)
        {
            context.Database.ExecuteSqlCommand("sp_delete_finding_int @finding_id = {0}", id);
        }

        public void SaveChanges()
        {
            context.SaveChanges();
        }
    }

So here is the weird part. finding.Audit.StatusID is getting updated in the DB. finding.FindingStatusId is not.

So for the entity, "finding" that I am sending to the repo's Update method, the "FindingStatusId" for the entity being updated is not getting saved. But, the "finding" entity's parent, "Audit", the "StatusID" is getting saved.

I can't for the life of me figure out what is going on here.

For completeness I'll post the Finding and Audit Entity Models.

[Table("finding")]
    public class Finding
    {
        private string _findingText;

        [Key]
        [Column("finding_id")]
        public int FindingId { get; set; }

        [Column("finding_audit_id")]
        public int FindingAuditId { get; set; }

        [Column("finding_cd")]
        [Display(Name = "Finding #")]
        [StringLength(15)]
        public string FindingCd { get; set; }

        [Column("finding_tx")]
        [Required(ErrorMessage = "Description Required")]
        [StringLength(7000)]
        public string FindingText
        {
            get
            {
                return _findingText;
            }
            set
            {
                _findingText = value?.Trim();
            }
        }

        [Column("finding_page_cd")]
        [StringLength(100)]
        public string FindingPageCd { get; set; }

        [Column("finding_joint_cd")]
        public string FindingJointCd { get; set; }

        [Column("finding_compliance_tx")]
        [StringLength(20)]
        public string FindingComplianceText { get; set; }

        [Column("finding_prior_year_cd")]
        [Display(Name = "Repeat Finding")]
        public string FindingPriorYearCd { get; set; }

        [Column("finding_decision_cd")]
        public string FindingDecisionCd { get; set; }

        [Column("finding_request_decision_cd")]
        public string FindingRequestDecisionCd { get; set; }

        [Column("finding_decision_ogc_concur_cd")]
        public string FindingDecisionOgcConcurCd { get; set; }

        [Column("finding_pdl_id")]
        public int? FindingPdlId { get; set; }

        [Display(Name = "Significant")]
        [Column("finding_significant_cd")]
        public string FindingSignificantCd { get; set; }

        [Column("finding_on_stay_cd")]
        public string FindingOnStayCd { get; set; }

        [Column("finding_stay_request_cd")]
        public string FindingStayRequestCd { get; set; }

        [Column("finding_last_update_dt")]
        public DateTime FindingLastUpdateDate { get; set; }

        [Column("finding_update_staff_id")]
        public short? FindingUpdateStaffId { get; set; }

        [Column("finding_cd_org")]
        public string FindingCdOrg { get; set; }

        [NotMapped]
        public string RepeatingYearsDisplayList
        {
            get
            {
                if (RepeatingYears?.Count > 0)
                {
                    string repeatingYears = string.Empty;
                    RepeatingYears.ForEach(ry =>
                        repeatingYears += $"{ry.FindingFyCd}, ");
                    return repeatingYears.Remove(repeatingYears.Length - 2);
                }
                return string.Empty;
            }
        }

        #region Navigation Properties
        [Column("finding_finding_type_id")]
        public short? FindingTypeId { get; set; }
        [ForeignKey("FindingTypeId")]
        public FindingType FindingType { get; set; }

        [Column("finding_status_id")]
        public int? FindingStatusId { get; set; }
        [ForeignKey("FindingStatusId")]
        public Status FindingStatus { get; set; }

        public List<FindingFiscalYear> RepeatingYears { get; set; }
        public List<Recommendation> Recommendations { get; set; }

        [ForeignKey("FindingAuditId")]
        public Audit Audit { get; set; }
        #endregion
    }



[Table("audit")]
    public class Audit
    {
        private string _auditAcnCd;
        private string _title;
        private string _summary;

        [Key]
        [Column("audit_id")]
        public int AuditID { get; set; }

        [Required(ErrorMessage = "ACN Required")]
        [Display(Name="ACN:")]
        [Column("audit_acn_cd")]
        public string AuditAcnCd
        {
            get
            {
                return _auditAcnCd;
            }
            set
            {
                _auditAcnCd = value?.Trim();
            }
        }

        [Required(ErrorMessage = "Title Required")]
        [Display(Name = "Title:")]
        [Column("audit_report_title_tx")]
        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value?.Trim();
            }
        }

        [Required(ErrorMessage = "Issuer Required")]
        [Display(Name="Issuer:")]
        [Column("audit_issuer_tx")]
        public string Issuer { get; set; }

        [Display(Name = "Sensitive Designation")]
        [Column("audit_sensitive_cd")]
        public string AuditSensitiveCode { get; set; }

        [Display(Name = "Alternative Product")]
        [Column("audit_alternate_product_cd")]
        public string AuditAlternateProductCode { get; set; }

        [RegularExpression("([1-9][0-9]*)", ErrorMessage = "Priority must be a number.")]
        [Display(Name = "Priority:")]
        [Column("audit_priority_cd")]
        public short? Priority { get; set; }

        [StringLength(maximumLength: 1000,ErrorMessage = "Max Length: 1000")]
        [Display(Name = "Summary:")]
        [Column("audit_summary_tx")]
        public string Summary
        {
            get
            {
                return _summary;
            }
            set
            {
                _summary = value?.Trim();
            }
        }

        [Column("audit_gao_contact_tx")]
        [Display(Name = "GAO Contact:")]
        [StringLength(maximumLength: 200, ErrorMessage = "Max Length: 200")]
        public string AuditGaoContactText { get; set; }

        [Column("audit_gao_job_cd")]
        [Display(Name = "GAO Job Code:")]
        [StringLength(maximumLength: 200, ErrorMessage = "Max Length: 30")]
        public string AuditGaoJobCode { get; set; }

        [Display(Name = "Lead Office:")]
        [Column("audit_lead_office_id")]
        public short? LeadOfficeID { get; set; }

        #region Navigation Properties
        [Required(ErrorMessage = "Audit Type Required.")]
        [Display(Name = "Audit Type:")]
        [Column("audit_audit_type_id")]
        public short AuditTypeID { get; set; }
        [Display(Name = "Audit Type:")]
        public AuditType AuditType { get; set; }

        [Column("audit_status_id")]
        public int StatusID { get; set; } 
        public Status Status { get; set; }

        [Required(ErrorMessage = "Office is Required.")]
        [Display(Name = "Offices:")]
        [Column("audit_office_id")]
        public short? OfficeID { get; set; }
        public Office Office { get; set; }

        [ForeignKey("AuditID")]
        public External External { get; set; }

        public IEnumerable<AuditLog> AuditLogs { get; set; }
        public IEnumerable<Finding> Findings { get; set; }
        public IEnumerable<Assignment> Assignments { get; set; }

        [Column("audit_update_staff_id")]
        public short UpdateStaffID { get; set; }

        [Column("audit_oig_manager_id")]
        [Display(Name = "OIG Audit Manager:")]
        public short? OigAuditManagerId { get; set; }
        [Display(Name = "OIG Audit Manager:")]
        [ForeignKey("OigAuditManagerId")]
        public Staff OigAuditManager { get; set; }

        [Column("audit_fsa_office_id")]
        [Display(Name = "FSA Audit Lead:")]
        public int? FsaLeadOfficeId { get; set; }
        [Display(Name = "FSA Audit Lead:")]
        [ForeignKey("FsaLeadOfficeId")]
        public FSAOffice FsaLeadOffice { get; set; }

        [ForeignKey("LeadOfficeID")]
        public Office LeadOffice { get; set; }
        #endregion
    }

Upvotes: 1

Views: 474

Answers (1)

Sam
Sam

Reputation: 5637

Well just as I was hoping I opened it back up this morning, started fresh, and it's working.

Ivan, I believe you are right. You don't need to call Update for Entity Framework. I've noticed this before if I forget the Update statement.

And if you don't need to you are making a call to update in the repo for no reason.

I was resetting scenarios by hand in the DB to test this over and over. Something must have gotten corrupt.

But all the code above is working if it helps anyone.

Upvotes: 1

Related Questions