Entity Framework가 자식 개체를 저장 / 삽입하지 못하도록하려면 어떻게해야합니까?
엔터티 프레임 워크로 엔터티를 저장할 때 자연스럽게 지정된 엔터티 만 저장하려고한다고 가정했습니다. 그러나 해당 엔터티의 하위 엔터티도 저장하려고합니다. 이로 인해 모든 종류의 무결성 문제가 발생합니다. EF가 저장하려는 엔터티 만 저장하여 모든 자식 개체를 무시하도록하려면 어떻게해야합니까?
속성을 수동으로 null로 설정하면 "작업 실패 : 하나 이상의 외래 키 속성이 null이 아니기 때문에 관계를 변경할 수 없습니다."라는 오류가 발생합니다. 자식 개체를 특별히 null로 설정하여 EF가 그대로두기 때문에 이것은 매우 비생산적입니다.
자식 개체를 저장 / 삽입하지 않는 이유는 무엇입니까?
이것은 주석에서 앞뒤로 논의되고 있으므로 왜 내 자식 개체를 그대로두기를 원하는지에 대한 정당성을 제공하겠습니다.
내가 구축중인 애플리케이션에서 EF 개체 모델은 데이터베이스에서로드되지 않고 플랫 파일을 구문 분석하는 동안 채우는 데이터 개체로 사용됩니다. 자식 개체의 경우 이러한 개체 중 다수는 부모 테이블의 다양한 속성을 정의하는 조회 테이블을 참조합니다. 예를 들어 기본 엔티티의 지리적 위치입니다.
이러한 개체를 직접 채웠으므로 EF는 이러한 개체를 새 개체로 가정하고 부모 개체와 함께 삽입해야합니다. 그러나 이러한 정의는 이미 존재하며 데이터베이스에 중복을 생성하고 싶지 않습니다. EF 개체를 사용하여 조회를 수행하고 주 테이블 엔터티에 외래 키를 채 웁니다.
실제 데이터 인 자식 개체를 사용하더라도 부모를 먼저 저장하고 기본 키를 가져와야합니다. 그렇지 않으면 EF가 엉망인 것 같습니다. 이것이 약간의 설명을 제공하기를 바랍니다.
내가 아는 한 두 가지 옵션이 있습니다.
옵션 1)
모든 자식 개체가 Null이면 EF가 아무것도 추가하지 않도록합니다. 또한 데이터베이스에서 아무것도 삭제하지 않습니다.
옵션 2)
다음 코드를 사용하여 자식 개체를 컨텍스트에서 분리 된 것으로 설정합니다.
context.Entry(yourObject).State = EntityState.Detached
List
/는 분리 할 수 없습니다 Collection
. 목록을 반복하고 목록의 각 항목을 이렇게 분리해야합니다.
foreach (var item in properties)
{
db.Entry(item).State = EntityState.Detached;
}
간단히 말해서 외래 키를 사용하면 하루를 절약 할 수 있습니다.
학교 법인과 도시 법인 이 있다고 가정 하고 이것은 도시에 많은 학교가 있고 학교가 도시에 속하는 다 대일 관계입니다. 그리고 도시가 이미 조회 테이블에 존재하므로 새 학교를 삽입 할 때 도시가 다시 삽입되는 것을 원하지 않습니다.
처음에는 다음과 같이 엔티티를 정의 할 수 있습니다.
public class City
{
public int Id { get; set; }
public string Name { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public City City { get; set; }
}
그리고 다음 과 같이 School 삽입을 수행 할 수 있습니다 (이미 newItem에 City 속성이 할당되어 있다고 가정 ).
public School Insert(School newItem)
{
using (var context = new DatabaseContext())
{
context.Set<School>().Add(newItem);
// use the following statement so that City won't be inserted
context.Entry(newItem.City).State = EntityState.Unchanged;
context.SaveChanges();
return newItem;
}
}
위의 접근 방식은이 경우 완벽하게 작동 할 수 있지만, 더 명확하고 유연한 외래 키 접근 방식을 선호합니다 . 아래 업데이트 된 솔루션을 참조하십시오.
public class City
{
public int Id { get; set; }
public string Name { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("City_Id")]
public City City { get; set; }
[Required]
public int City_Id { get; set; }
}
이러한 방식으로 학교 에 외래 키 City_Id 가 있고 City 엔티티를 참조하도록 명시 적으로 정의합니다 . 따라서 School 삽입과 관련하여 다음을 수행 할 수 있습니다.
public School Insert(School newItem, int cityId)
{
if(cityId <= 0)
{
throw new Exception("City ID no provided");
}
newItem.City = null;
newItem.City_Id = cityId;
using (var context = new DatabaseContext())
{
context.Set<School>().Add(newItem);
context.SaveChanges();
return newItem;
}
}
이 경우 새 레코드 의 City_Id 를 명시 적으로 지정하고 그래프 에서 City 를 제거하여 EF가 School 과 함께 컨텍스트에 추가하지 않도록합니다 .
첫인상에서는 외래 키 접근 방식이 더 복잡해 보이지만이 사고 방식은 다 대다 관계를 삽입 할 때 많은 시간을 절약 해 줄 것입니다 (학교와 학생 관계가 있다고 상상해보십시오. City 속성이 있음) 등등.
이것이 당신에게 도움이되기를 바랍니다.
If you just want to store changes to a parent object and avoid storing changes to any of its child objects, then why not just do the following:
using (var ctx = new MyContext())
{
ctx.Parents.Attach(parent);
ctx.Entry(parent).State = EntityState.Added; // or EntityState.Modified
ctx.SaveChanges();
}
The first line attaches the parent object and the whole graph of its dependent child objects to the context in Unchanged
state.
The second line changes the state for the parent object only, leaving its children in the Unchanged
state.
Note that I use a newly created context, so this avoids saving any other changes to the database.
One of the suggested solutions is to assign the navigation property from the same database context. In this solution, the navigation property assigned from outside the database context would be replaced. Please, see following example for illustration.
class Company{
public int Id{get;set;}
public Virtual Department department{get; set;}
}
class Department{
public int Id{get; set;}
public String Name{get; set;}
}
Saving to database:
Company company = new Company();
company.department = new Department(){Id = 45};
//an Department object with Id = 45 exists in database.
using(CompanyContext db = new CompanyContext()){
Department department = db.Departments.Find(company.department.Id);
company.department = department;
db.Companies.Add(company);
db.SaveChanges();
}
Microsoft enlists this as a feature, however I find this annoying. If the department object associated with company object has Id that already exists in database, then why doesn't EF just associates company object with database object? Why should we need to take care of the association by ourselves? Taking care of the navigation property during adding new object is something like moving the database operations from SQL to C#, cumbersome to the developers.
First you need to know that there are two ways for updating entity in EF.
Attached objects
When you change the relationship of the objects attached to the object context by using one of the methods described above, the Entity Framework needs to keep foreign keys, references, and collections in sync.
Disconnected objects
If you are working with disconnected objects you must manually manage the synchronization.
In the application I'm building, the EF object model is not being loaded from the database but used as data objects which I'm populating while parsing a flat file.
That means you are working with disconnected object, but it's unclear whether you are using independent association or foreign key association.
Add
When adding new entity with existing child object (object that exists in the database), if the child object is not tracked by EF, the child object will be re-inserted. Unless you manually attach the child object first.
db.Entity(entity.ChildObject).State = EntityState.Modified; db.Entity(entity).State = EntityState.Added;
Update
You can just mark the entity as modified, then all scalar properties will be updated and the navigation properties will simply be ignored.
db.Entity(entity).State = EntityState.Modified;
Graph Diff
If you want to simplify the code when working with disconnected object, you can give a try to graph diff library.
Here is the introduction, Introducing GraphDiff for Entity Framework Code First - Allowing automated updates of a graph of detached entities.
Sample Code
Insert entity if it doesn't exist, otherwise update.
db.UpdateGraph(entity);
Insert entity if it doesn't exist, otherwise update AND insert child object if it doesn't exist, otherwise update.
db.UpdateGraph(entity, map => map.OwnedEntity(x => x.ChildObject));
Best way to do this is in by overriding the SaveChanges function in your datacontext.
public override int SaveChanges()
{
var added = this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Added);
// Do your thing, like changing the state to detached
return base.SaveChanges();
}
This worked for me:
// temporarily 'detach' the child entity/collection to have EF not attempting to handle them
var temp = entity.ChildCollection;
entity.ChildCollection = new HashSet<collectionType>();
.... do other stuff
context.SaveChanges();
entity.ChildCollection = temp;
I know it's old post however if you are using code-first approach you may achieve the desired result by using following code in your mapping file.
Ignore(parentObject => parentObject.ChildObjectOrCollection);
This will basically tell EF to exclude "ChildObjectOrCollection" property from model so that it is not mapped to database.
I've got the same problem when I trying to save profile, I already table salutation and new to create profile. When I insert profile it also insert into salutation. So I tried like this before savechanges().
db.Entry(Profile.Salutation).State = EntityState.Unchanged;
What we have done is before adding the parent to the dbset, disconnect the child collections from the parent, making sure to push the existing collections to other variables to allow working with them later, and then replacing the current child collections with new empty collections. Setting the child collections to null/nothing seemed to fail for us. After doing that then add the parent to the dbset. This way the children are not added until you want them to.
'Nice programing' 카테고리의 다른 글
JVM의 Haskell? (0) | 2020.10.05 |
---|---|
초기화되지 않은 개체와 NULL로 초기화 된 개체 (0) | 2020.10.05 |
일반 프로토콜을 변수 유형으로 사용하는 방법 (0) | 2020.10.05 |
Java에서 객체를 null에 할당하면 가비지 수집에 영향을 줍니까? (0) | 2020.10.05 |
ContextMenuStrip이 사용 된 컨트롤 확인 (0) | 2020.10.05 |