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

asp.net-core-mvc-2.0 entity-framework-core

Question

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
    }
1
0
8/10/2018 9:43:18 PM

Popular Answer

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.

1
8/11/2018 12:12:39 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow