누구든지 Entity Framework를 사용하여 "삽입 행이 있으면 삽입 행"논리를 구현하는 가장 효율적인 방법에 대한 제안이 있습니까?
누구든지 Entity Framework를 사용하여 "삽입 행이 있으면 삽입 행"논리를 구현하는 가장 효율적인 방법에 대한 제안이 있습니까?
답변:
연결된 객체 (동일한 컨텍스트 인스턴스에서로드 된 객체)로 작업하는 경우 간단히 다음을 사용할 수 있습니다.
if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
context.MyEntities.AddObject(myEntity);
}
// Attached object tracks modifications automatically
context.SaveChanges();
객체의 키에 대한 지식을 사용할 수 있다면 다음과 같이 사용할 수 있습니다.
if (myEntity.Id != 0)
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
ID로 객체의 존재를 결정할 수 없으면 조회 조회가 필요합니다.
var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
using
블록 에 넣습니다 . 컨텍스트를 잠시 동안 메모리에 두는 것이 괜찮습니까? 예를 들어, Windows 양식 수명 동안? 일반적으로 데이터베이스에 대한로드를 최소화하기 위해 데이터베이스 개체를 정리하려고합니다. EF 컨텍스트를 파괴하기 위해 대기하는 데 문제가 없습니까?
Entity Framework 4.3부터 AddOrUpdate
네임 스페이스 에는 메소드가 있습니다 System.Data.Entity.Migrations
.
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
어떤 의사에 의해 :
SaveChanges가 호출 될 때 키로 엔티티를 추가하거나 업데이트합니다. 데이터베이스 용어의 "upsert"작업과 같습니다. 이 방법은 마이그레이션을 사용하여 데이터를 시드 할 때 유용 할 수 있습니다.
@ Smashing1978 의 의견에 답하기 위해 @Colin에서 제공하는 링크에서 관련 부품을 붙여 넣습니다.
AddOrUpdate의 작업은 개발 중에 데이터를 시드 할 때 복제본을 만들지 않도록하는 것입니다.
먼저, 키 (첫 번째 매개 변수)로 제공 한 항목이 AddOrUpdate에 제공된 맵핑 된 열 값과 일치하는 레코드를 찾기 위해 데이터베이스에서 쿼리를 실행합니다. 따라서 이것은 시합에 대한 약간 느슨한 끈적이지만 시딩 설계 시간 데이터에는 완벽하게 좋습니다.
더 중요한 것은 일치하는 항목이 발견되면 업데이트가 모두 업데이트되고 AddOrUpdate에없는 항목은 모두 제거한다는 것입니다.
즉, 외부 서비스에서 데이터를 가져 와서 기본 키로 기존 값을 삽입하거나 업데이트하고 소비자에 대한 로컬 데이터는 읽기 전용입니다. AddOrUpdate
현재 6 개월 이상 프로덕션 환경 에서 사용 하고 있습니다. 문제 없습니다.
마법은 전화 할 때 발생 SaveChanges()
하며 전류에 따라 다릅니다 EntityState
. 엔터티가 EntityState.Added
이면 데이터베이스에 추가되고, 있으면 엔터티 EntityState.Modified
에서 업데이트됩니다. 따라서 InsertOrUpdate()
다음과 같이 메소드를 구현할 수 있습니다 .
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
Id = 0
새로운 엔티티인지 여부를 확인할 수 없으면 Ladislav Mrnka 의 답변을 확인하십시오 .
동일한 컨텍스트를 사용하고 엔티티를 분리하지 않는 경우 다음과 같이 일반 버전을 만들 수 있습니다.
public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
if (db.Entry(entity).State == EntityState.Detached)
db.Set<T>().Add(entity);
// If an immediate save is needed, can be slow though
// if iterating through many entities:
db.SaveChanges();
}
db
물론 클래스 필드가 될 수도 있고, 메소드를 정적으로 확장 할 수도 있지만 이것이 기본입니다.
Ladislav의 대답은 가까웠지만 EF6 (데이터베이스 우선)에서 작동하도록 몇 가지 수정 작업을 수행해야했습니다. onAddOrUpdate 메소드를 사용하여 데이터 컨텍스트를 확장했으며 지금까지는 분리 된 객체에서 잘 작동하는 것으로 보입니다.
using System.Data.Entity;
[....]
public partial class MyDBEntities {
public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
if (ID != 0) {
set.Attach(obj);
ctx.Entry(obj).State = EntityState.Modified;
}
else {
set.Add(obj);
}
}
[....]
내 의견으로는 Entity Framework Code에 대해 새로 릴리스 된 EntityGraphOperations를 사용 하면 그래프에서 모든 엔티티의 상태를 정의하기 위해 반복적 인 코드를 작성하지 않아도 됩니다. 나는이 제품의 저자입니다. 그리고 github , code-project ( 단계별 데모 포함 및 샘플 프로젝트 다운로드 준비 완료) 및 nuget에 게시했습니다. .
그것은 것이다 자동으로 개체의 상태를 설정 에 Added
나 Modified
. 더 이상 존재하지 않는 경우 삭제할 엔티티를 수동으로 선택합니다.
예를 들면 :
내가 Person
물건을 가지고 있다고 가정 해 봅시다 . Person
많은 전화, 문서, 배우자가있을 수 있습니다.
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public int Age { get; set; }
public int DocumentId {get; set;}
public virtual ICollection<Phone> Phones { get; set; }
public virtual Document Document { get; set; }
public virtual PersonSpouse PersonSpouse { get; set; }
}
그래프에 포함 된 모든 엔티티의 상태를 결정하고 싶습니다.
context.InsertOrUpdateGraph(person)
.After(entity =>
{
// Delete missing phones.
entity.HasCollection(p => p.Phones)
.DeleteMissingEntities();
// Delete if spouse is not exist anymore.
entity.HasNavigationalProperty(m => m.PersonSpouse)
.DeleteIfNull();
});
또한 아시다시피 전화 엔터티의 상태를 정의하는 동안 고유 키 속성이 역할을 수행 할 수 있습니다. 그러한 특별한 목적을 위해 우리는 ExtendedEntityTypeConfiguration<>
클래스를 상속받습니다 EntityTypeConfiguration<>
. 이러한 특수 구성을 사용하려면에서 ExtendedEntityTypeConfiguration<>
대신 매핑 클래스를 상속해야합니다 EntityTypeConfiguration<>
. 예를 들면 다음과 같습니다.
public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
{
public PhoneMap()
{
// Primary Key
this.HasKey(m => m.Id);
…
// Unique keys
this.HasUniqueKey(m => new { m.Prefix, m.Digits });
}
}
그게 다야.
다른 것을 삽입하면 둘 다 업데이트
public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();
var query = from data in context.Employee
orderby data.name
select data;
foreach (Employee details in query)
{
if (details.id == 1)
{
//Assign the new values to name whose id is 1
details.name = "Sanjay";
details. Surname="Desai";
details.address=" Desiwadi";
}
else if(query==null)
{
details.name="Sharad";
details.surname=" Chougale ";
details.address=" Gargoti";
}
}
//Save the changes back to database.
context.SaveChanges();
}
기존 행을 모두 확인하십시오.
public static void insertOrUpdateCustomer(Customer customer)
{
using (var db = getDb())
{
db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
}
@LadislavMrnka 답변의 대안. 이것은 Entity Framework 6.2.0의 경우입니다.
특정 DbSet
항목과 업데이트 또는 생성해야하는 항목이있는 경우 :
var name = getNameFromService();
var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
_dbContext.Names.Add(name);
}
else
{
_dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();
그러나 DbSet
단일 기본 키 또는 복합 기본 키가 있는 일반에도 사용할 수 있습니다 .
var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");
public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
foreach (var value in values)
{
try
{
var keyList = new List<object>();
//Get key values from T entity based on keyValues property
foreach (var keyValue in keyValues)
{
var propertyInfo = value.GetType().GetProperty(keyValue);
var propertyValue = propertyInfo.GetValue(value);
keyList.Add(propertyValue);
}
GenericAddOrUpdateDbSet(keyList, value);
//Only use this when debugging to catch save exceptions
//_dbContext.SaveChanges();
}
catch
{
throw;
}
}
_dbContext.SaveChanges();
}
public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
//Get a DbSet of T type
var someDbSet = Set(typeof(T));
//Check if any value exists with the key values
var current = someDbSet.Find(keyList.ToArray());
if (current == null)
{
someDbSet.Add(value);
}
else
{
Entry(current).CurrentValues.SetValues(value);
}
}
수정
public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity)
where T : class
where T2 : class
{
foreach(var e in updateEntity)
{
context.Set<T2>().InsertOrUpdate(e);
}
}
public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity)
where T : class
where T2 : class
{
if (context.Entry(updateEntity).State == EntityState.Detached)
{
if (context.Set<T2>().Any(t => t == updateEntity))
{
context.Set<T2>().Update(updateEntity);
}
else
{
context.Set<T2>().Add(updateEntity);
}
}
context.SaveChanges();
}