programing tip

Entity Framework를 사용하여 다른 삽입 논리가있는 경우 행 업데이트

itbloger 2020. 6. 1. 19:10
반응형

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();

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 개월 이상 프로덕션 환경 에서 사용 하고 있습니다. 문제 없습니다.


동일한 컨텍스트를 사용하고 엔티티를 분리하지 않는 경우 다음과 같이 일반 버전을 만들 수 있습니다.

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 물론 클래스 필드가 될 수 있거나 메소드를 정적 및 확장으로 만들 수 있지만 이것이 기본입니다.


마법은 전화 할 때 발생 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(); 
    } 
}

EntityState에 대한 추가 정보

Id = 0새로운 엔티티인지 여부를 확인할 수없는 경우 Ladislav Mrnka답변을 확인하십시오 .


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에 게시했습니다 .

그것은 것이다 자동으로 개체의 상태를 설정AddedModified. 더 이상 존재하지 않는 경우 삭제할 엔티티를 수동으로 선택합니다.

예를 들면 :

내가 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();
}

@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();

However this can also be used for a generic DbSet with a single primary key or a composite primary key.

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);
    }
}

Check existing row with Any.

    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();

        }

    }

Corrected

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();
        }

참고URL : https://stackoverflow.com/questions/5557829/update-row-if-it-exists-else-insert-logic-with-entity-framework

반응형