Entity Framework 6을 사용하여 레코드를 업데이트하는 방법은 무엇입니까?


245

EF6을 사용하여 레코드를 업데이트하려고합니다. 먼저 레코드를 찾으면 업데이트하십시오. 내 코드는 다음과 같습니다.

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

위의 코드를 사용하여 레코드를 업데이트하려고 할 때 마다이 오류가 발생합니다.

{System.Data.Entity.Infrastructure.DbUpdateConcurrencyException : 저장 업데이트, 삽입 또는 삭제 명령문이 예상치 않은 행 수 (0)에 영향을 미쳤습니다. 엔터티가로드 된 후 엔터티가 수정되거나 삭제되었을 수 있습니다. ObjectStateManager 항목 새로 고침


7
참고 : catch (Exception ex){throw;}중복되지 않으므로 완전히 제거 할 수 있습니다.
Sriram Sakthivel

catch catch 블록은 실패 이유를 파악하는 것입니다. 그러나 여전히이 코드가 실패하는 이유는 무엇입니까?
user1327064

2
이 주제의 전문가는 아니지만이 질문에 대답 할 수 없습니다. 그러나 try catch없이 예외가 발생 하면 break 를 사용하여 예외가 있을 때 디버거를 중단 할 수 있습니다 .
Sriram Sakthivel

1
아무것도 변경하지 않았습니다. 엔터티 상태로 재생해도 개체가 실제로 수정되지 않았다는 사실은 변경되지 않습니다.
Jonathan Allen

1
글쎄, 나는 당신과 똑같이했고 오류를 얻지 못했습니다. 예외는 DbUpdateConcurrencyException을 나타냅니다. 동시성을 어떻게 처리 했습니까? 타임 스탬프를 사용했거나 개체를 복제 한 다음 다시 병합 했습니까? 아니면 자체 추적 엔터티를 사용 했습니까? (가장 많이 사용되는 3 가지 방법). 동시성을 처리하지 않았다면 이것이 문제인 것 같습니다.
El Mac

답변:


345

레코드를 업데이트하려고합니다 ( "기존 레코드의 값을 변경하고 다시 저장하십시오"라는 의미). 따라서 오브젝트를 검색하고 변경 한 후 저장해야합니다.

using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        result.SomeValue = "Some new value";
        db.SaveChanges();
    }
}

16
값을 할당해도 데이터베이스가 업데이트되지 않으며 db.SaveChanges()컨텍스트에서 수정 된 개체를 호출 하면 데이터베이스가 업데이트됩니다.
Craig W.

6
여전히 나를 매혹시킵니다 ... var 결과는 실제로 dbcontext에 연결됩니다 ... 따라서 dbcontext 멤버가 인스턴스화 한 모든 변수는 실제로 해당 변수를 데이터베이스에 연결하여 변경 사항이 해당 변수에 적용됨을 의미합니다 또한 적용되거나 지속됩니까?
WantIt

6
컨텍스트가 오브젝트를 생성 했으므로 컨텍스트는 오브젝트 변경 사항을 포함하여 오브젝트를 추적 할 수 있습니다. SaveChanges컨텍스트 를 호출 하면 추적중인 모든 오브젝트를 평가하여 추가, 변경 또는 삭제되었는지 판별하고 연결된 데이터베이스에 적절한 SQL을 발행합니다.
Craig W.

3
동일한 문제에 직면 한 iam-EF6 사용, 엔터티 업데이트 첨부 + EntityState. 수정이 작동하지 않습니다. 작동하는 것만입니다. 객체를 검색하고 원하는대로 변경 한 후 db.SaveChanges ()를 통해 저장해야합니다.
Gurpreet Singh

7
업데이트하기 위해 먼저 개체를 검색 할 필요는 없습니다. 기본 키 값 중 하나 (복합 키)를 변경하려고 시도 할 때까지 동일한 문제가 발생했습니다. 올바른 기본 키를 제공하는 한 EntityState를 Modified로 설정하면 테이블에 정의 된 다른 무결성 제약 조건을 위반하지 않으면 SaveChanges ()가 작동합니다.
adrianz

166

Entity Framework의 소스 코드를 검토하고 Key 속성을 알고 있으면 실제로 엔티티를 업데이트하는 방법을 찾았습니다.

public void Update<T>(T item) where T: Entity
{
    // assume Entity base class have an Id property for all items
    var entity = _collection.Find(item.Id);
    if (entity == null)
    {
        return;
    }

    _context.Entry(entity).CurrentValues.SetValues(item);
}

그렇지 않으면 아이디어가 있는지 AddOrUpdate 구현을 확인하십시오 .

이 도움을 바랍니다!


12
좋은! 모든 속성을 열거 할 필요는 없습니다. SaveChanges()값을 설정 한 후 호출이 필요 하다고 가정 합니다.
Jan Zahradník

3
예, SaveChanges ()에서 변경 사항이 유지됩니다
Miguel

1
대단한 대답은 IntelliSense에서 이와 같은 작업을 수행해도 작동하지 않을 것이라는 점은 분명하지 않았습니다. _context.MyObj = newObj; 그런 다음 SaveChanges () 또는 .... _context.MyObj.Update (newObj) 그리고 SaveChanges (); 솔루션은 모든 속성을 반복하지 않고도 전체 개체를 업데이트합니다.
Adam

7
이것은 ID 필드를 편집하려고한다고 불평합니다
Vasily Hall

3
@VasilyHall-ID 필드 (또는 기본 키를 정의한 항목)가 모델 (모델 중 하나에 null / 0 포함)이 다른 경우에 발생합니다. ID가 두 모델간에 일치하는지 확인하면 제대로 업데이트됩니다.
Gavin Coates

51

다음 AddOrUpdate방법을 사용할 수 있습니다 .

db.Books.AddOrUpdate(book); //requires using System.Data.Entity.Migrations;
db.SaveChanges();

1
IMO 최고의 솔루션
Norgul

112
.AddOrUpdate()데이터베이스 마이그레이션 중에 사용되므로 마이그레이션 외부에서이 방법을 사용하지 않는 것이 좋습니다. 따라서 Entity.Migrations네임 스페이스 에있는 이유 입니다.
Adam Vincent

1
@AdamVincent가 말했듯이, AddOrUpdate()메소드는 마이그레이션을위한 것이며 기존 행을 업데이트 해야하는 상황에는 적합하지 않습니다. 검색 참조가 포함 된 책이없는 경우 (예 : ID) 새 행을 생성하고 문제가 발생할 수 있습니다 (예 : API가 있으면 404-NotFound 응답을 반환해야 함) 존재하지 않는 행에 대해 PUT 메소드를 호출하십시오).
Marko

4
당신이하고있는 것을 알지 않는 한 이것을 사용하지 마십시오 !!!!!!!!!!!!!!!! 읽기 : michaelgmccarthy.com/2016/08/24/…
Yusha

4
나는 오늘 이것으로 다시 돌아 왔고, 이것이 당신이 이것이 원하는 사용 사례에 대한 좋은 해결책 이 아니라고 경고 할 수 있을까
Yusha

23

따라서 업데이트 된 엔티티가 있으며 가장 적은 양의 코드로 데이터베이스에서 업데이트하려고합니다 ...

동시성은 항상 까다 롭지 만 업데이트를 받기를 원한다고 가정합니다. 다음은 같은 경우에 어떻게했고 클래스를 흉내 내기 위해 이름을 수정 한 방법입니다. 즉,로 변경 attach하면 add나를 위해 작동합니다.

public static void SaveBook(Model.Book myBook)
{
    using (var ctx = new BookDBContext())
    {
        ctx.Books.Add(myBook);
        ctx.Entry(myBook).State = System.Data.Entity.EntityState.Modified;
        ctx.SaveChanges();
    }
}

10

객체의 모든 필드를 업데이트하려는 경우 Entry () 메서드를 사용해야합니다. 또한 필드 ID (키)를 변경할 수 없으므로 먼저 ID를 편집 한 것과 동일하게 설정하십시오.

using(var context = new ...())
{
    var EditedObj = context
        .Obj
        .Where(x => x. ....)
        .First();

    NewObj.Id = EditedObj.Id; //This is important when we first create an object (NewObj), in which the default Id = 0. We can not change an existing key.

    context.Entry(EditedObj).CurrentValues.SetValues(NewObj);

    context.SaveChanges();
}

2
코드를 게시하는 것이 아니라 최소한 질문에 답해야합니다
StaceyGirl

질문을 더 잘 이해하기 위해 코드 스 니펫을 남기지 않고 질문에 대해 설명하십시오.
feanor07

9

이 코드는 쿼리에서 레코드를 먼저 반환하지 않고 열 집합 만 업데이트하는 테스트 결과입니다. 먼저 Entity Framework 7 코드를 사용합니다.

// This function receives an object type that can be a view model or an anonymous 
// object with the properties you want to change. 
// This is part of a repository for a Contacts object.

public int Update(object entity)
{
    var entityProperties =  entity.GetType().GetProperties();   
    Contacts con = ToType(entity, typeof(Contacts)) as Contacts;

    if (con != null)
    {
        _context.Entry(con).State = EntityState.Modified;
        _context.Contacts.Attach(con);

        foreach (var ep in entityProperties)
        {
            // If the property is named Id, don't add it in the update. 
            // It can be refactored to look in the annotations for a key 
            // or any part named Id.

            if(ep.Name != "Id")
                _context.Entry(con).Property(ep.Name).IsModified = true;
        }
    }

    return _context.SaveChanges();
}

public static object ToType<T>(object obj, T type)
{
    // Create an instance of T type object
    object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

    // Loop through the properties of the object you want to convert
    foreach (PropertyInfo pi in obj.GetType().GetProperties())
    {
        try
        {
            // Get the value of the property and try to assign it to the property of T type object
            tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
        }
        catch (Exception ex)
        {
            // Logging.Log.Error(ex);
        }
    }
    // Return the T type object:         
    return tmp;
}

다음은 완전한 코드입니다.

public interface IContactRepository
{
    IEnumerable<Contacts> GetAllContats();
    IEnumerable<Contacts> GetAllContactsWithAddress();
    int Update(object c);
}

public class ContactRepository : IContactRepository
{
    private ContactContext _context;

    public ContactRepository(ContactContext context)
    {
        _context = context;
    }

    public IEnumerable<Contacts> GetAllContats()
    {
        return _context.Contacts.OrderBy(c => c.FirstName).ToList();
    }

    public IEnumerable<Contacts> GetAllContactsWithAddress()
    {
        return _context.Contacts
            .Include(c => c.Address)
            .OrderBy(c => c.FirstName).ToList();
    }   

    //TODO Change properties to lambda expression
    public int Update(object entity)
    {
        var entityProperties = entity.GetType().GetProperties();

        Contacts con = ToType(entity, typeof(Contacts)) as Contacts;

        if (con != null)
        {
            _context.Entry(con).State = EntityState.Modified;
            _context.Contacts.Attach(con);

            foreach (var ep in entityProperties)
            {
                if(ep.Name != "Id")
                    _context.Entry(con).Property(ep.Name).IsModified = true;
            }
        }

        return _context.SaveChanges();
    }

    public static object ToType<T>(object obj, T type)
    {
        // Create an instance of T type object
        object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

        // Loop through the properties of the object you want to convert
        foreach (PropertyInfo pi in obj.GetType().GetProperties())
        {
            try
            {
                // Get the value of the property and try to assign it to the property of T type object
                tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
            }
            catch (Exception ex)
            {
                // Logging.Log.Error(ex);
            }
        }
        // Return the T type object
        return tmp;
    }
}    

public class Contacts
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Company { get; set; }
    public string Title { get; set; }
    public Addresses Address { get; set; }    
}

public class Addresses
{
    [Key]
    public int Id { get; set; }
    public string AddressType { get; set; }
    public string StreetAddress { get; set; }
    public string City { get; set; }
    public State State { get; set; }
    public string PostalCode { get; set; }  
}

public class ContactContext : DbContext
{
    public DbSet<Addresses> Address { get; set; } 
    public DbSet<Contacts> Contacts { get; set; } 
    public DbSet<State> States { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connString = "Server=YourServer;Database=ContactsDb;Trusted_Connection=True;MultipleActiveResultSets=true;";
        optionsBuilder.UseSqlServer(connString);
        base.OnConfiguring(optionsBuilder);
    }
}

7

.net 코어

context.Customer.Add(customer);
context.Entry(customer).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
context.SaveChanges();

5

이 문제에 대한 최상의 해결책은 다음과 같습니다.보기에서 모든 ID (키)를 추가하십시오. 이름이 여러 개인 테이블 (첫 번째, 두 번째 및 세 번째)을 고려하십시오.

@Html.HiddenFor(model=>model.FirstID)
@Html.HiddenFor(model=>model.SecondID)
@Html.HiddenFor(model=>model.Second.SecondID)
@Html.HiddenFor(model=>model.Second.ThirdID)
@Html.HiddenFor(model=>model.Second.Third.ThirdID)

C # 코드에서

[HttpPost]
public ActionResult Edit(First first)
{
  if (ModelState.Isvalid)
  {
    if (first.FirstID > 0)
    {
      datacontext.Entry(first).State = EntityState.Modified;
      datacontext.Entry(first.Second).State = EntityState.Modified;
      datacontext.Entry(first.Second.Third).State = EntityState.Modified;
    }
    else
    {
      datacontext.First.Add(first);
    }
    datacontext.SaveChanges();
    Return RedirectToAction("Index");
  }

 return View(first);
}

5

Attach엔티티를 추적하면 추적 상태가로 설정됩니다 Unchanged. 기존 엔터티를 업데이트하려면 추적 상태를로 설정하면됩니다 Modified. EF6 문서 에 따르면 :

데이터베이스에 이미 존재하지만 변경이 이루어진 엔티티가있는 경우 컨텍스트에 엔티티를 첨부하고 상태를 수정 됨으로 설정할 수 있습니다. 예를 들면 다음과 같습니다.

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

4
using(var myDb = new MyDbEntities())
{

    user user = new user();
    user.username = "me";
    user.email = "me@me.com";

    myDb.Users.Add(user);
    myDb.users.Attach(user);
    myDb.Entry(user).State = EntityState.Modified;//this is for modiying/update existing entry
    myDb.SaveChanges();
}

4

잘 작동하는 방법을 찾았습니다.

 var Update = context.UpdateTables.Find(id);
        Update.Title = title;

        // Mark as Changed
        context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();


1

다음은 Post-RIA 엔티티 업데이트 방법입니다 (Ef6 시간대).

public static void UpdateSegment(ISegment data)
{
    if (data == null) throw new ArgumentNullException("The expected Segment data is not here.");

    var context = GetContext();

    var originalData = context.Segments.SingleOrDefault(i => i.SegmentId == data.SegmentId);
    if (originalData == null) throw new NullReferenceException("The expected original Segment data is not here.");

    FrameworkTypeUtility.SetProperties(data, originalData);

    context.SaveChanges();
}

참고 FrameworkTypeUtility.SetProperties()내가 NuGet에 AutoMapper 오래 전에 쓴 작은 유틸리티 기능은 다음과 같습니다

public static void SetProperties<TIn, TOut>(TIn input, TOut output, ICollection<string> includedProperties)
    where TIn : class
    where TOut : class
{
    if ((input == null) || (output == null)) return;
    Type inType = input.GetType();
    Type outType = output.GetType();
    foreach (PropertyInfo info in inType.GetProperties())
    {
        PropertyInfo outfo = ((info != null) && info.CanRead)
            ? outType.GetProperty(info.Name, info.PropertyType)
            : null;
        if (outfo != null && outfo.CanWrite
            && (outfo.PropertyType.Equals(info.PropertyType)))
        {
            if ((includedProperties != null) && includedProperties.Contains(info.Name))
                outfo.SetValue(output, info.GetValue(input, null), null);
            else if (includedProperties == null)
                outfo.SetValue(output, info.GetValue(input, null), null);
        }
    }
}

참고 : 속성이 모델에 저장된 ViewModel 객체와 정확히 동일한 경우에만 작동합니다.
vapcguy

1

Renat가 말했듯이 다음을 제거하십시오. db.Books.Attach(book);

또한이 쿼리는 엔터티 프레임 워크의 모델 상태를 무시하므로 "AsNoTracking"을 사용하도록 결과 쿼리를 변경하십시오. "결과"는 지금 추적 할 책이라고 생각하며 원하지 않습니다.

var result = db.Books.AsNoTracking().SingleOrDefault(b => b.BookNumber == bookNumber);

1

시도 해봐....

UpdateModel (책);

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            UpdateModel(book);
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

1

나는 그것이 이미 몇 번 잘 대답했다는 것을 알고 있지만, 아래의 방법을 좋아합니다. 누군가에게 도움이되기를 바랍니다.

//attach object (search for row)
TableName tn = _context.TableNames.Attach(new TableName { PK_COLUMN = YOUR_VALUE});
// set new value
tn.COLUMN_NAME_TO_UPDATE = NEW_COLUMN_VALUE;
// set column as modified
_context.Entry<TableName>(tn).Property(tnp => tnp.COLUMN_NAME_TO_UPDATE).IsModified = true;
// save change
_context.SaveChanges();

1

이것은 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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.