seeding one to many relationship with entity framework - entity-framework

I've looked through quite a few threads about seeding a one to many relationship with EF but can't seem to find the answer to what seems like a simple question. How to do it? I have the following code where I'm trying to create an AuctionItem entity and then add AuctionImage entities to it. But I get a null exception for auctionOne.AuctionImages on the line auctionOne.AuctionImages.Add()... Can anyone tell me what I'm doing wrong? Thanks!
protected override void Seed(AuctionDbContext db)
{
var auctionOne = new AuctionItem()
{
AuctionComplete = string.Empty,
AuctionDate = DateTime.Now.AddDays(-1),
CurrentPrice = 2,
EndPrice = 5,
InitialPrice = 1,
InitialQuantity = 1,
LongDescription = "Long description",
PriceDrops = 2,
QuantityRemaining = 2,
ReserveQuantity = 1,
RetailPrice = 4,
ShortDescription = "Short description",
Title = "Auction one"
};
auctionOne.AuctionImages.Add(new AuctionImage
{
Description = "Beautiful picture",
Filename = "picture.jpg"
});
db.AuctionItems.Add(auctionOne);
base.Seed(db);
}
And here are my classes.
public class AuctionItem
{
[Key]
public int AuctionItemID { get; set; }
[Column(TypeName = "varchar"), MaxLength(256)]
public string Title { get; set; }
public decimal InitialPrice { get; set; }
public int InitialQuantity { get; set; }
public DateTime? AuctionDate { get; set; }
[Column(TypeName = "varchar(max)")]
public string ShortDescription { get; set; }
[Column(TypeName = "varchar(max)")]
public string LongDescription { get; set; }
public decimal RetailPrice { get; set; }
public decimal? EndPrice { get; set; }
public decimal CurrentPrice { get; set; }
public int PriceDrops { get; set; }
public int QuantityRemaining { get; set; }
public int ReserveQuantity { get; set; }
[Column(TypeName = "char"), MaxLength(10)]
public string AuctionComplete { get; set; }
[Column(TypeName = "xml")]
public string Metadata { get; set; }
public virtual ICollection<AuctionImage> AuctionImages { get; set; }
}
public class AuctionImage
{
[Key]
public int AuctionImageID { get; set; }
[ForeignKey("AuctionItem")]
public int AuctionItemID { get; set; }
public virtual AuctionItem AuctionItem { get; set; }
[Column(TypeName = "varchar"), MaxLength(256)]
public string Description { get; set; }
[Column(TypeName = "varchar(max)")]
public string Filename { get; set; }
[Column(TypeName = "xml")]
public string MetaData { get; set; }
}

You haven't allocated the collection for AuctionImages before you are referencing it with the Add, which is why you get the null exception. (When the object is first created, the property will have a null value).
For seeding, it's usually just easiest to do something like:
var auctionOne = new AuctionItem()
{
AuctionComplete = string.Empty,
AuctionDate = DateTime.Now.AddDays(-1),
// ...
AuctionImages = new List<AuctionImage> {
new AuctionImage { Description="", Filename="" },
new AuctionImage { Description="", Filename="" }
}
};
If you want to do it as two separate steps, just allocate the AuctionImages property as a new List<> (or other ICollection) before adding to it:
var auctionOne = new AuctionItem()
{
// ...
Title = "Auction one"
};
auctionOne.AuctionImages = new List<AuctionImage>(); // add this
auctionOne.AuctionImages.Add(new AuctionImage
{
Description = "Beautiful picture",
Filename = "picture.jpg"
});
db.AuctionItems.Add(auctionOne);

Related

Entity Framework .Core treating foreign key as a primary key

I have the two classes shown below. When trying to insert two HeroEntry with the same IdentityId, the context is only saving one of the two entries. Any ideas what I am doing wrong?
public class Identity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IdentityId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
[JsonIgnore]
public virtual HeroEntry HeroEntry { get; set; }
}
public class HeroEntry
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int HeroEntryId { get; set; }
public int IdentityId { get; set; }
public SuperHeroEnum HeroType { get; set; }
public DateTime Startdate { get; set; }
public DateTime EndDate { get; set; }
[ForeignKey("IdentityId")]
public virtual Identity Identity { get; set; }
public virtual List<SuperHeroBreak> SuperHeroBreaks { get; set; }
}
Add code:
var b = new HeroEntry()
{
HeroType = SuperHeroEnum.BATMAN,
Startdate = DateTime.Parse("12/24/2018"),
EndDate = DateTime.Parse("01/04/2019"),
IdentityId = 1,
};
var r = new HeroEntry()
{
HeroType = SuperHeroEnum.ROBIN,
Startdate = DateTime.Parse("12/24/2018"),
EndDate = DateTime.Parse("01/04/2019"),
IdentityId = 1,
};
context.SuperHeroes.AddRange(b, r);
var affected = context.SaveChanges();
Cristian was correct. Adding the navigation property to the identity class solved my issue. Thank you for the help!

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
}
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.

The property 'Id' is part of the object's key information and cannot be modified after the second insert

I have a table with Id as primary key
The model generated by EF is this
public partial class Home_Orchard_Users_UserPartRecord
{
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string NormalizedUserName { get; set; }
public string Password { get; set; }
public string PasswordFormat { get; set; }
public string HashAlgorithm { get; set; }
public string PasswordSalt { get; set; }
public string RegistrationStatus { get; set; }
public string EmailStatus { get; set; }
public string EmailChallengeToken { get; set; }
public Nullable<System.DateTime> CreatedUtc { get; set; }
public Nullable<System.DateTime> LastLoginUtc { get; set; }
public Nullable<System.DateTime> LastLogoutUtc { get; set; }
}
When I tried to insert a data from a list, the first insert was okay but the second one I got error: The property 'Id' is part of the object's key information and cannot be modified.
foreach (var ContentItem in ListContentItem)
{
ContentItemData.Data = ContentItem.Data;
ContentItemData.ContentType_id = 3;
Context.Home_Orchard_Framework_ContentItemRecord.Add(ContentItemData);
Context.SaveChanges();
int Return_Id = ContentItemData.Id;
UserData.Id = Return_Id;
UserData.UserName = ContentItem.UserName;
UserData.Email = ContentItem.Email;
UserData.NormalizedUserName = ContentItem.NormalizedUserName;
UserData.Password = "AMQ6a4CzqeyLbWqrd7EwoaTV23fopf++3FpcBlV+Gsvgja3Ye3LwFlXVHyhAcWyKaw==";
UserData.PasswordFormat = "Hashed";
UserData.HashAlgorithm = "PBKDF2";
UserData.PasswordSalt = "FaED9QsIV9HD95m8FO5OSA==";
UserData.RegistrationStatus = "Approved";
UserData.EmailStatus = "Approved";
UserData.CreatedUtc = DateTime.Today;
Context.Home_Orchard_Users_UserPartRecord.Add(UserData);
//Context.SaveChanges();
i = i + 1;
progress = ((float)i / nCount) * 100;
Console.Write("\r{0}%", progress);
Thread.Sleep(50);
}
Why I can't insert the second time? The Id is the primary key and it's not autogenerated so it has to be input manually.

Adding new related entities in a single action

Every riddle has one or more questions, how can add both a Riddle and a Question to that riddle by submitting a single form?
This is RiddlesController Create action code:
public ActionResult Create(RiddleViewModel model)
{
if (ModelState.IsValid)
{
try
{
_db.Riddles.Add(new Models.Riddle
{
Name = model.Name,
Description = model.Description ,
CreationDate = DateTime.Now,
User = _db.Users.Find(User.Identity.GetUserId()),
});
_db.Questions.Add(new Models.Question
{
Body = model.FirstQuestionBody,
Answer = model.FirstQuestionAnswer,
CreationDate = DateTime.Now,
// What should I write here? or is there any better way to accomplish this?
Riddle = ?????
});
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
return View();
}
This is Riddle model code:
public class Riddle
{
public int Id { get; set; }
public string Name { get; set; }
[MaxLength(200)]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
public List<Review> Reviews { get; set; }
[Required]
public ApplicationUser User { get; set; }
public virtual List<Question> Questions { get; set; }
[Column(TypeName = "datetime2")]
public DateTime CreationDate { get; set; }
}
This is Question model code:
public class Question
{
public int Id { get; set; }
public string Body { get; set; }
public string Answer { get; set; }
public Riddle Riddle { get; set; }
[Column(TypeName ="datetime2")]
public DateTime CreationDate { get; set; }
}
This is RiddleViewModel code:
public class RiddleViewModel
{
[Required]
public string Name { get; set; }
[MaxLength(200)]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
// Question properties
[DataType(DataType.MultilineText)]
public string FirstQuestionBody { get; set; }
public string FirstQuestionAnswer { get; set; }
}
You can try as shown below.
_db.Questions.Add(new Models.Question
{
Body = model.FirstQuestionBody,
Answer = model.FirstQuestionAnswer,
CreationDate = DateTime.Now,
Riddle = new Models.Riddle
{
Name = model.Name,
Description = model.Description ,
CreationDate = DateTime.Now,
User = _db.Users.Find(User.Identity.GetUserId()),
}
});
_db.SaveChanges();

EF code first property not mapped

The problem I'm encountering is when I try to insert a new record in a ASPxGridView which is a master of detail in an asp.net page.
This only occurs when adding a new record is required when there is no record.
EnderecoEscola entity:
namespace DAL
{
[Table("CAD_ENDERECO_ESCOLA")]
public class EnderecoEscola
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ENDESC_ID { get; set; }
[Required]
public int ESCOLA_ID { get; set; }
[Association("Escolas", "ESCOLA_ID", "ESCOLA_ID")]
[ForeignKey("ESCOLA_ID")]
public virtual Escola Escola { get; set; }
[Required]
public int TPOEND_ID { get; set; }
[ForeignKey("TPOEND_ID")]
public virtual TipoEndereco TipoEndereco { get; set; }
[Required]
public int ENDESC_UF_ID { get; set; }
[ForeignKey("ENDESC_UF_ID")]
public virtual UnidadeFederativa UnidadeFederativa { get; set; }
[Required]
public int ENDESC_MUN_iD { get; set; }
[ForeignKey("ENDESC_MUN_iD")]
public virtual Municipio Municipio { get; set; }
[StringLength(10), Required]
[MinLength(8)]
public string ENDESC_CEP { get; set; }
[StringLength(100), Required]
[MinLength(10)]
public string ENDESC_ENDERECO { get; set; }
[StringLength(15)]
public string ENDESC_NRO { get; set; }
[StringLength(25)]
public string ENDESC_COMPL { get; set; }
[StringLength(70)]
public string ENDESC_BAIRRO { get; set; }
[NotMapped]
public String TPEND_DESCRICAO { get; set; }
[NotMapped]
public String UF_SIGLA { get; set; }
[NotMapped]
public String MUN_DESCRICAO { get; set; }
}
}
DAL :
namespace DAL.utilities
{
public class OperationCadEnderecoEscola
{
public IQueryable<EnderecoEscola> GetId(int idEsc)
{
using (SecurityCtx ctx = new SecurityCtx())
{
ctx.Configuration.LazyLoadingEnabled = false;
var query = ctx.EnderecoEscola.Include("TipoEndereco").Include("UnidadeFederativa").Include("Municipio").Where(w => w.ESCOLA_ID == idEsc).OrderBy(p => p.ENDESC_ENDERECO).ToList().
Select(w => new EnderecoEscola
{
ENDESC_ID = w.ENDESC_ID,
ESCOLA_ID = w.ESCOLA_ID,
TPOEND_ID = w.TPOEND_ID,
ENDESC_UF_ID = w.ENDESC_UF_ID,
ENDESC_MUN_iD = w.ENDESC_MUN_iD,
ENDESC_CEP = w.ENDESC_CEP,
ENDESC_ENDERECO = w.ENDESC_ENDERECO,
ENDESC_NRO = w.ENDESC_NRO,
ENDESC_COMPL = w.ENDESC_COMPL,
ENDESC_BAIRRO = w.ENDESC_BAIRRO,
TPEND_DESCRICAO = w.TipoEndereco.TPEND_DESCRICAO != null ? w.TipoEndereco.TPEND_DESCRICAO : w.TPEND_DESCRICAO,
UF_SIGLA = w.UnidadeFederativa.UF_SIGLA != null ? w.UnidadeFederativa.UF_SIGLA : w.UF_SIGLA,
MUN_DESCRICAO = w.Municipio.MUN_DESCRICAO != null ? w.Municipio.MUN_DESCRICAO : w.MUN_DESCRICAO
}).Distinct().AsQueryable();
return query;
}
}
}
}
When applying for inclusion in ASPxGridView a new record and the method in DAL public IQueryable <EnderecoEscola> getId (int idEsc) is invoked to retrieve the data and they do not exists it is adding a new record on the master and detail occurs error
A field or property with name 'TPEND_DESCRICAO' was not found in the
selected data source.
Someone could guide me on how to solve the problem.
Tks.