Prevent EF from trying to update/insert child objects [duplicate] - entity-framework

This question already has answers here:
How do I stop Entity Framework from trying to save/insert child objects?
(13 answers)
Closed 3 years ago.
I have an Address object that has a City property. When creating a brand new Address to be inserted via EF6, I fill all required basic Address properties (address line 1, postal code, etc), but I don't need a fully hydrated City instance so long as it has the ID like so:
address.City = new City { Id = 1 };
When I attempt to insert my Address, it also attempts to do validation on the City's properties, but I don't want to do any CRUD on City since all I need is its ID.
I found the below question that introduced me to detaching entries from the DbContext so that EF does not attempt to do CRUD on said objects:
How do I stop Entity Framework from trying to save/insert child objects?
What seems to be happening when I detach the City instance is that it also nulls it out, so my Address has a null City property. This is a problem because City is also required, so a DbEntityValidationException is thrown saying "The City field is required".
I am new to EF, so perhaps the way I am going about all of this is just wrong to begin with.
Edit By request, here's all my code:
Building my Address entity in my client before passing it to WebApi endpoint:
var user = new AppUser { Id = 1 };
var address = new Address
{
City = new City { Id = 277 },
Line1 = "123 whatever ln",
PostalCode = "01233",
CreatedBy = user,
ModifiedBy = user,
CreatedOn = DateTime.Today,
ModifiedOn = DateTime.Today
};
In my ASP.NET app, I create an array of instances I want to detach from the context:
Detached = new object[] {
value.Principle.ModifiedBy,
value.Principle.CreatedBy,
value.Principle.City
};
Just before the save, I detach all instances in the array:
foreach (var d in DetachedObjects)
{
dbContext.Entry(d).State = EntityState.Detached;
}
dbContext.SaveChanges();
What I thought was happening with detaching properties was that it was simply telling EF not to do any CRUD on them, but I didn't want it to null them out because I want the parent/principle entity to have the ID for its FK.
Here are my Address and City classes:
[DebuggerDisplay("{Line1}")]
public class Address : CisEntity
{
[MaxLength(200)]
[Required]
public string Line1 { get; set; }
[MaxLength(200)]
public string Line2 { get; set; }
[Required]
public City City { get; set; }
[MaxLength(25)]
public string PostalCode { get; set; }
}
[DebuggerDisplay("{Name}, {Province.Name}, {Province.Country.Name}")]
public class City : CisEntity, IEntityName
{
[Required]
public Province Province { get; set; }
[MaxLength(100)]
public string Name { get; set; }
}

If you don't want the City to be required when performing CRUD, remove the required attribute. If you truly need a City for your Address in your program, but not in the database, then do as you are and null out the City piece before performing CRUD on Address. Otherwise it will be inserted. You should go check out what your tables look like in your database. EF will be tracking these in separate tables, and the City column on Address will be a foreign key. If you decorate your City property on Address with the required attribute, it means that the column is non-nullable. This means that in the database this column must contain a foreign key to a record in the City table, hence a City must exist.

I believe I understand your question and I'm answering 3 years later. LOL.
For the main entity,
context.Addresses.Add(address);
for the related or child entities,
context.Entry(address.City).State = EntityState.Modified;
Do this for every other related entity.

Related

EF6 - Adding new entities which belong to an already persisted entity

Suppose I have a Company with a list of Employees:
public class Company
{
public List<Employee> Employees { get; set; }
}
public class Employee
{
public string Name { get; set; }
public Company CompanyOfWork { get; set; }
}
My Company already exists in the DB, but I need to save some new Employees.
Currently I would loop through the list of Employees, looking for new ones which need saving to the DB (in my case, ID = 0), and add them:
foreach(var employee in company.Employees)
{
if(employee.ID == 0)
db.Employees.Add(employee);
}
This creates the new Employees in the DB, but it also creates a duplicated record of the Company in the DB (presumably because EF looks at CompanyOfWork on Employee, and assumes it needs adding to the DB?)
What is the correct way to do this to prevent it from creating duplicates?
Do I need to first check if company already exists, and attach it if so? None of my suggestions feel very clean.
Add the employees to the existing company, then save the company, instead of adding them directly to the DbSet:
var existingCompany = db.Companies.Find(companyId);
foreach (var employee in employeesToAdd) {
if (! existingCompany.Employees.Contains(employee)) {
existingCompany.Employees.Add(employee);
}
else {
// update existing employee ...
}
}
db.SaveChanges();
EF ChangeTracker will know that it also has to update the references between company and employee, if the relationship is modeled correctly for the persistable entities. This code also assumes that lazy loading of employees is enabled.

Entity Framework 7 (Beta 7) and Entity Tracking

I am trying to separate my domain model (which is responsible for my business logic) with my data model which is what EF 7 uses to build my schema.
I am running into a problem with regards to persisting changes made to my domain model to my database
So for example given that I have the Data Model PersonTable and the Domain model Person:
public class PersonTable
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
public virtual String Surname { get; set; }
}
public class Person
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
public virtual String Surname { get; set; }
//Other Domain Methods and Constructors...
}
And I want to persist my domain changes to my database by doing this:
public void Update(Person p)
{
var person = new PersonTable
{
Id = p.Id,
Name = p.Name,
Surname = p.Surname
}
PersonDbContext.Update(person);
PersonDbContext.SaveChanges();
}
When I try to apply these changes I get an InvalidOperationException saying
"The instance of entity type 'Tables.PersonTable' cannot be tracked because another instance of this type with the same key is already being tracked. For new entities consider using an IIdentityGenerator to generate unique key values."
I assume this has something to do with entity tracking but how does this work in EF 7? I want to be able to apply this pattern so that I can seperate these two models.
// Before updating we have fetch original data row from database.
// So we need to use AsNoTracking().
var originalPerson = PersonDbContext.Person.AsNoTracking().FirstOrDefault(p => p.Id == id);
and at time of save we need to use following code.
PersonDbContext.Entry(originalPerson).Context.Update(newPersonDetail);
The error occurs when your instance of PersonDbContext is already tracking an instance of Person with the same Id.
To debug this, inspect what you have tracking already in PersonDbContext.ChangeTracker.Entries(). I expect you will discover you already have an entry for PersonTable.Id that matches the one you are attempting to update.
To ensure you don't get these conflicts, don't reuse PersonDbContext across threads or multiple HTTP requests (if this is a web app.)
I was facing the same error. I found the answer in this other thread here
https://stackoverflow.com/a/31840042/1716912. I changed my repository DI to be scoped from being a singleton.

Entity framework, find method leaves foreign key empty

I have an entity named PageItem. PageItem has a property named Page. Type of Page is Page class.
class PageItem {
public Page Page { get; set; }
...
}
when I query like this:
var item = context.PageItems.Find(5);
Problem is, item.Page is null, so when I save item entity framework creates a new page record.
Summary of what I learned:
First: entity framework Find method does not fill Id value of nested objects (in database language: foreign keys. But I realized than when you save that entity, it does not update foreign key columns, so nothing is broken.
Seconly: if you want to read, and use foregin key values of an entity, you should define (int/long whatever) properties per referanced table an foreign key. And mark it with ForeignKey attribute. Also if it can be null, make property type int? or long? (nullable)
Sample:
pubclic class Customer {
public Id { get; set; }
[ForeignKey("City")}
public int? City_Id { get; set; }
public City City { get; set; }
}
Also if you want layz loading on City, you have to mark it virtual.

Entity Framework identity column always zero

I'm using following class to insert products to database.
ID column is primary key.
After adding multiple products to db context (without calling savechanges method) all newly added rows identity columns are zero!
My scene...
User adds several products and browse them on the data grid.
User selects one product and adds some barcodes to selected product.
When user finishes the job clicks on save button and application calls SaveChanges method!
When user wants to add some barcodes to products firstly I need to find selected product from context and adds entered barcode text to Barcodes list. But I cant do that because all products identity columns value are the same and they are zero.
How can I solve this problem?
public class Product
{
public int ProductID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public virtual List<Barcode> Barcodes { get; set; }
}
public class Barcode
{
public int BarcodeID { get; set; }
public string BarcodeText { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
}
Identity column value is assigned by database when you are inserting record into table. Before you call SaveChanges no queries are executed, nothing is passed to database and back. Context just keeps in-memory collection of entities with appropriate state (state defines which time of query should be executed during changes saving - for new entities, which have Added state, insert query should be generated and executed). So, ID stays with its default value, which is zero for integer. You should not give value manually. After inserting entities into database, context will receive ID value and update entity.
UPDATE: When you are adding Barcode to existing product, then EF is smart enough to update keys and foreign keys of entities:
var product = db.Products.First(); // some product from database
var barcode = new Barcode { BarcodeText = "000" };
// at this point barcode.ID and barcode.ProductID are zeros
product.Barcodes.Add(barcode);
db.SaveChanges(); // execute insert query
// at this point both PK and FK properties will be updated by EF

With Entity Framework code-first, how to Insert object with existing property

Here is the simple case.
I have a Customer record defined like this:
public class Customer
{
[Key]
public string Id { get;}
public UsState UsState { get; set; }
}
public class UsState
{
[Key]
public string StateAbbreviation {get;set;}
public string StateName {get;set;}
}
I already have my UsState's populated with the 50 states, now I want to insert one Customer record that ties to an existing state.
If I say something like:
Customer customer = new Customer()
{
UsState = "CA",
Id = 1001
}
dbContext.Customers.add(customer);
I get an error saying state is trying to be inserted again.
How can I get the add to use an existing state and not try and re-insert it. My real problem has lots of nested objects that may exist so I need to solve this in general.
Thanks
Two ways I know is:
Set EntityState to what is appropriate for your action or
Get the UsState from the database and add it to the customer right before saving, which will give it an EntityState of UnModified or something like that.
to change the EntityState you need to get to the ObjectContext of your DbContext like shown in this thread: Entity Framework Code First - No Detach() method on DbContext
If you think about this in terms of table data, your Customer table should have a column in it that describes the customer's state. You also have a state table that you can use to join together to get the state name.
CREATE TABLE customer (
id VARCHAR(10),
state CHAR(2)
)
CREATE TABLE state (
stateabbreviation CHAR(2),
statename VARCHAR(50)
)
The "state" column is a just a string representing the state. So your class should be defined the same way, with and ID and a State, and if you want to include the information for your USState class, defined a property of type UsState with a ForeignKey defined:
public class Customer
{
[Key]
public string Id { get;}
public string State { get; set; }
[ForeignKey("State")]
public UsState UsState { get; set; }
}
Then, when you create a new record, set the text of the string, not the UsState object.
Customer customer = new Customer()
{
State = "CA",
Id = 1001
}
dbContext.Customers.add(customer);