Can't insert entity with foreign key in Entity Framework - c#

I'm building Backend for Mobile Application with ASP.NET MVC Framework.
I have two Objects:
public class CarLogItem : EntityData
{
public CarLogItem(): base()
{
Time = DateTime.Now;
}
public DateTime Time { get; set; }
public int RPM { get; set; }
public int Speed { get; set; }
public int RunTime { get; set; }
public int Distance { get; set; }
public int Throttle { get; set; }
[ForeignKey("Trip")]
public String Trip_id { get; set; }
// Navigation property
public TripItem Trip { get; set; }
}
and
public class TripItem : EntityData
{
public TripItem() : base()
{
UserId = User.GetUserSid();
StartTime = DateTime.Now;
logItems = new List<CarLogItem>();
}
public string UserId { get; set; }
public List<CarLogItem> logItems {get;set;}
public DateTime StartTime { get; set; }
}
and I have controller, which add new CarLogItem to database.
public class CarLogItemController : TableController<CarLogItem>
{
// POST tables/CarLogItem
public async Task<IHttpActionResult> PostCarLogItem(CarLogItem item)
{
var lastItem = db.CarLogItems.OrderByDescending(x => x.Time).FirstOrDefault();
//lastItem = (Query().Where(logitem => true).OrderBy(logitem => logitem.Time)).Last();
//checking if lastItem.Trip isn't null because
// I have entities with Trip field is null, but all of them should have it.
if (lastItem != null && lastItem.Trip != null && item.RunTime > lastItem.RunTime)
{
item.Trip = lastItem.Trip;
}
//In order to test adding of new TripItem entity to database
// I compare item.RunTime with 120, so it always true
else if (lastItem == null || item.RunTime < 120) // < lastItem.RunTime)
{
var newTrip = new TripItem();
item.Trip = newTrip;
}
else
{
throw new ArgumentException();
}
CarLogItem current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
}
When I'm trying to add new CarLogItem with Trip = null it's ok, but when Trip is particular object it fails with following Exception:
The entity submitted was invalid: Validation error on property 'Id': The Id field is required
How properly to add new CarLogItem with nested TripItem?

I think that you need to populate the Id property on your TripItem, e.g.
var newTrip = new TripItem(){ Id = Guid.NewGuid() }

You need a primary key field in every entity class, like Id or CarLogItemId (ClassName + "Id"). Or just have a property with [Key] attribute:
[Key]
public string/int/Guid/any-db-supported-type MyProp { get; set; }
Entity Framework relies on every entity having a key value that it
uses for tracking entities. One of the conventions that code first
depends on is how it implies which property is the key in each of the
code first classes. That convention is to look for a property named
“Id” or one that combines the class name and “Id”, such as “BlogId”.
The property will map to a primary key column in the database.
Please see this for more details.
I also suspect this to be a problem:
public Lazy<CarLogItem> logItems { get; set; }
You don't have to mark navigation property as Lazy<>. It is already lazy (unless you have configuration that disables lazy loading). Please try to remove Lazy<> and see if it works this way.

Related

How can I specify to add item to the database without also adding the foreign key?

I have the following Models:
public interface Item
{
public int Id { get; set; }
}
public class ComponentOwner : Item
{
public int Id { get; set; }
public Component Component { get; set; }
public AppUser? User { get; set; }
public DateTime ModifiedDate { get; set; }
public AppUser? UpdatedBy { get; set; }
}
public class AppUser : IdentityUser
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
}
and the following async Task that saves the item to the database:
private async Task<Item> SaveItem(Item item)
{
Item updatedItem = null;
using var context = _dbContextFactory.CreateDbContext();
try
{
if (item.Id == 0)
{
context.Add(item);
await context.SaveChangesAsync();
}
When I Save a new ComponentOwner, context.Add(Item) adds the item, but also tries to add a new 'AppUser' at the same time. This causes issues because the AppUser is already created.
Is there a way that I can specify to add the ComponentOwner but not the AppUser?
as soon as 'Context.Add(item)' is hit, it wants to add an AppUser as well as the Component. I only want it to add the ComponentOwner however..
EF Core relies on tracking to determine what to do with entities. In this case it seems that item.User is not tracked, so EF tries to add it. There are multiple possible solution to this. To name a few:
If you are sure that user exists, you can just attach the entity:
if(item.User is not null)
context.Users.Attach(item.User); // or just context.Attach(item.User);
Fetch user from database and assign it to the root entity:
if (item.User is not null)
{
var user = context.Users.FirstOrDefault(u => u.Id == item.User.Id); // TODO: handle null
item.User = user;
}
Use Find:
Finds an entity with the given primary key values. If an entity with the given primary key values is being tracked by the context, then it is returned immediately without making a request to the database. Otherwise, a query is made to the database for an entity with the given primary key values and this entity, if found, is attached to the context and returned. If no entity is found, then null is returned.
if (item.User is not null)
{
var user = context.Users.Find(item.User.Id); // TODO: handle null
item.User = user;
}

Generate an unique string for a field in a pre-defined pattern when save using Code First Approch: Entity Framework

I have a table called Quotations and it has an unique string field called QuotationNo.
public class Quotation
{
[Key]
public int Id { get; set; } //PK
public string QuotationNo{ get; set; } // Pattern QUOT/12-22/00001 => QUOT + MONTH-YEAR + PrimaryKey.ToStrin("5d");
}
So I have implemented it by calling _db.SaveChanges() method twice as follows.
if (ModelState.IsValid)
{
_db.Quotations.Add(quot);
_db.SaveChanges(); // 1 time saving
quot.QuotationNo= "QUOT/" + DateTime.Today.ToString("dd-MM/") + quot.Id.ToString("D5");
_db.Quotations.Update(quot);
_db.SaveChanges(); // 2 time saving
}
Is there a short way to do this?
I found this method is more clear.
Step 1: Creating new field in the table to store DateCreated.
Step 2: Making QuotationNo a virtual and NotMapped field, which is always a calculated/generated field.
See the code
public class Quotation
{
[Key]
public int Id { get; set; } //PK
[Required]
public DateTime DateCreated { get; set; } //New field to store quotation created date.
[NotMapped]
public virtual string? QuotationNo //Ax Key(Quotation No: QUOT/01-22/0000001=> PQTN/mmYY/0000000)
{
get
{
return $"QUOT/{this.DateCreated:dd-MM/}{PolyCostingId:D5}";
}
set
{
this.PolyCostingCode = value;
}
}
}
Please try this
public class Quotation
{
public int Id { get; set; } //PK
public string QuotationNo
{
get
{
return string.IsNullOrWhiteSpace(this.QuotationNo)
? $"QUOT/{DateTime.Today:dd-MM:hh:mm:ss/}{Id:D7}"
: this.QuotationNo;
}
set { this.QuotationNo = value; }
}}
For example
var x = new Quotation();
x.Id = 12345;
var quotationNo = x.QuotationNo;
// quotationNo will be QUOT/22-12/0012345
Some reference here Setting the default value of a DateTime Property to DateTime.Now inside the System.ComponentModel Default Value Attrbute

Having issues with a dependent property in a referential restraint

I keep getting this error when I try to submit to the database:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'NPPRProvId'
Here's where it breaks:
public static bool Save(NPPR_Provider provider, State state, string filename, bool validateBeforeSave = true )
{
using (var db = new NPPRContext(state))
{
var prov = new NPPR_Provider()
{
First = provider.First,
Middle = provider.Middle,
Last = provider.Last,
DateOfBirth = provider.DateOfBirth,
DateOfDeath = provider.DateOfDeath,
Gender = provider.Gender,
SSN = provider.SSN,
DegreeCode = provider.DegreeCode,
BusinessName = provider.BusinessName,
DbaName = provider.DbaName,
Action = "A",
EffectiveDate = "20121212",
EndDate = "99991231",
NPPR_ServLocation = new NPPR_ServLocation()
{
EnrollmentType = provider.NPPR_ServLocation.EnrollmentType,
OrganizationType = provider.NPPR_ServLocation.OrganizationType,
ProviderTypeCode = provider.NPPR_ServLocation.ProviderTypeCode,
IRSTaxAssociations = provider.NPPR_ServLocation.IRSTaxAssociations,
NPIAssociations = provider.NPPR_ServLocation.NPIAssociations,
Address = provider.NPPR_ServLocation.Address
},
NPPR_Header = new NPPR_Header()
{
FileName = filename,
TransactionDate = Utilities.DateTimeToPRNDate(DateTime.Now),
FileLoadDate = DateTime.Now,
SubmitterId = "M00000503",
Purpose = "A",
Action = "A"
}
};
foreach(var npi in prov.NPPR_ServLocation.NPIAssociations)
{
npi.NPIType = prov.NPPR_ServLocation.OrganizationType == "I" ? "1" : "2";
}
prov.NPPR_ServLocation.Licenses = SegmentOrNull<NPPR_Licenses>(provider.NPPR_ServLocation, "Licenses", "LicenseNumber");
prov.NPPR_ServLocation.Certifications = SegmentOrNull<NPPR_Certifications>(provider.NPPR_ServLocation, "Certifications", "CertificationNumber");
prov.NPPR_ServLocation.Specialties = SegmentOrNull<NPPR_Specialties>(provider.NPPR_ServLocation, "Specialties", "SpecialtyCode");
prov.NPPR_ServLocation.Taxonomies = SegmentOrNull<NPPR_Taxonomies>(provider.NPPR_ServLocation, "Taxonomies", "TaxonomyCode");
prov.NPPR_ServLocation.OtherIds = SegmentOrNull<NPPR_OtherIds>(provider.NPPR_ServLocation, "OtherIds", "IdentifierTypeId");
prov.NPPR_ServLocation.GroupAssociations = SegmentOrNull<NPPR_GroupAssociations>(provider.NPPR_ServLocation, "GroupAssociations", "ProviderLocationId");
db.NPPR_Provider.Add(prov);
if (validateBeforeSave)
db.SaveChangesWithValidation();
else
db.SaveChanges();
return true;
}
}
db.SaveChanges() is where, according to the stacktrace, the exception is thrown. This function was originally written for EF Core, but due to issues with the server, I was forced to turn everything to EF6.4. Under Core, this method worked fine, but under EF6, it throws an exception. I've tried a few things I've read on other SO questions but so far no luck.
Specifically the exception throws on the NPPR_Provider primary key, NPPRProvId, which at no point in my code is ever read or written, except to be defined in the model.
In SQL, the NPPRProvId is PK, int, not null
The model involved:
public class NPPR_Provider : Provider
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int NPPRProvId { get; set; }
//[ForeignKey("NPPR_Header")]
public int? NPPRFileId { get; set; }
[ForeignKey("NPPRFileId")]
public NPPR_Header NPPR_Header { get; set; }
[ForeignKey("NPPRProvId")]
public NPPR_ServLocation NPPR_ServLocation { get; set; }
}
What am I doing wrong here and how might I fix it? I'm still fairly new to EF and this is my first major project in EF and MVC, and in the .NET framework in general.
In EF Core the NPPR_ServLocation could be an owned object but to my knowledge in EF 6 there are not owned objects; you need to define them explicitly in a separate table and give them keys.
public class NPPR_Provider : Provider
{
public int NPPR_ProviderId { get; set; }
public NPPR_Header NPPR_Header { get; set; }
public NPPR_ServLocation NPPR_ServLocation { get; set; }
… // data properties here
}
public class NPPR_Header {
public int NPPR_HeaderId {get;set;}
public int NPPR_ProviderId {get;set;}
public NPPR_Provider {get;set;}
… // data properties here
}
public class NPPR_ServLocation {
public int NPPR_ServLocationId {get;set;}
public int NPPR_ProviderId {get;set;}
public NPPR_Provider {get;set;}
… // data properties here.
}

Update Unique property entity framework

public class Person
{
[Required]
public int? KupaId { get; set; }
[ForeignKey("KupaId")]
public Kupa Kupa { get; set; }
public int? newKupaId { get; set; }
[ForeignKey("newKupaId")]
public Kupa NewKupa { get; set; }
}
public class Kupa
{
public int Id { get; set; }
[Index("Ix_uniqueId", IsUnique = true)]
public int ? uniqueId { get; set; }
}
public class MyController:Controller
{
public Json EditKupa(Expression<Func<Person,bool>> criteria )
{
using (IKupotRepository<Person> _IPersonRepository = new SQlRepository<Person>())
{
Person personToEdit=_IPersonRepository.SingleOrDefault(criteria,GetIncludeProperties());
> //Getting the new kupa obj from db
newKupa = GetKupa(UniqueId);
<//changing the unique property to null
personToEdit.Kupa.ToremId = null;
personToEdit.Kupa.State = State.Modified;
personToEdit.NewKupa = newKupa;
>//Assign the unique id property the value that was in the first Kupa
personToEdit.NewKupa.ToremId = 1;
personToEdit.newKupaId = newKupa.Id;
personToEdit.NewKupa.State = State.Modified;
_IPersonRepository.SaveChanges();
}
}
when calling saveChanges() getting an exception :unique key violation , when looking at the sql profiler i can see that EF 6 generates an update query for both the Kupa object but it tries to update the NewKupa.uniqueId before updating the Kupa.uniqueId ?
Assuming you are using SQL Server as a database server this is happening because you allow NULL values in that column and NULL = NULL is NULL so if you have multiple rows with NULL on that column you'll get the error.
To implement this in SQL statements will be like this:
CREATE UNIQUE NONCLUSTERED INDEX Idx_UniqueId_NotNull
ON Kupa(uniqueId)
WHERE uniqueId IS NOT NULL;
However, to do this in EF there is no easy way, but there is a workaround in this SO answer here.

Saving primary and foreign keytables at once in EF6

I have primary table and 3 foreign key tables and trying to save all at once. Some time some OK and some time giving error.
public partial class meeting_abstract
{
public int meeting_abstract_id { get; set; }
public int meeting_id { get; set; }
public System.DateTime submission_date { get; set; }
public virtual ICollection<abstract_author> abstract_author { get; set; }
public virtual ICollection<abstract_category> abstract_category { get; set; }
public virtual ICollection<abstract_questions> abstract_questions { get; set; }
}
var meeting_abstract = new meeting_abstract();
meeting_abstract.meeting_id = meetingAbstract.Meeting.meeting_id;
meeting_abstract.submission_date = DateTime.Now;
meeting_abstract.abstract_questions = new Collection<abstract_questions>();
var abstractQuestion = new abstract_questions();
abstractQuestion.meeting_question_id = Convert.ToInt32(meetingAbstract.AbstractTitleInEnglishId);
abstractQuestion.abstract_question_answer = meetingAbstract.AbstractTitleInEnglishText;
meeting_abstract.abstract_questions.Add(abstractQuestion);
abstractQuestion = new abstract_questions();
abstractQuestion.meeting_question_id = Convert.ToInt32(meetingAbstract.AbstractTitleInLanguageId);
abstractQuestion.abstract_question_answer = meetingAbstract.AbstractTitleInLanguageText;
meeting_abstract.abstract_questions.Add(abstractQuestion);
meeting_abstract.abstract_author.Add(meetingAbstract.primaryAuthor);
var abstractCategory = new abstract_category()
{
meeting_category_id = meetingCategory.meeting_category_id
};
meeting_abstract.abstract_category = new Collection<abstract_category>();
meeting_abstract.abstract_category.Add(abstractCategory);
_abstractRewriteEntities.meeting_abstract.Add(meeting_abstract);
_abstractRewriteEntities.SaveChanges();
Whats wrong here? I cannot save all together?
When adding more than one item, with a property defined as the identity, you will need to give those entities a unique key even though it will ultimately be set by the db. We do this in our project by defining a partial class for that entity, and in OnCreated making sure they have a unique value for their identity.
For example:
public partial class abstract_questions
{
private static int _newIdentity = 0;
partial void OnCreated()
{
//Set an identity value so when two new entities
// are created Entity Framework doesn't whinge
// on the server because of duplicate keys
_newIdentity = _newIdentity - 1;
// Set whatever is defined as the identity/pk property
this.Id = _newIdentity;
}

Categories