EF6: Изменения, внесенные в контекст db, не распознаются [дубликат]

162
задан user1327064 17 September 2014 в 16:26
поделиться

14 ответов

Вы пытаетесь обновить запись (что для меня означает «изменить значение на существующую запись и сохранить ее обратно»). Поэтому вам нужно восстановить объект, внести изменения и сохранить его.

using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        result.SomeValue = "Some new value";
        db.SaveChanges();
    }
}
222
ответ дан Craig W. 18 August 2018 в 19:58
поделиться
  • 1
    W, как это точно работает? как присвоение значения «результату» фактически обновляет базу данных? – WantIt 3 March 2015 в 23:29
  • 2
    Присвоение значения не обновляет базу данных, вызывая db.SaveChanges() с измененными объектами в контексте, обновляет базу данных. – Craig W. 4 March 2015 в 00:06
  • 3
    Тем не менее, это меня завораживает ... так что результат var, фактически становится связанным с dbcontext ... поэтому это означает, что любая переменная, созданная любыми членами dbcontext, фактически будет иметь этот ассоциат в базе данных, чтобы любые изменения были применены к этой переменной , он также применяется или сохраняется? – WantIt 4 March 2015 в 15:30
  • 4
    Поскольку контекст генерирует объект, контекст может отслеживать объект, включая изменения объекта. Когда вы вызываете SaveChanges, контекст оценивает все объекты, которые он отслеживает, чтобы определить, были ли они добавлены, изменены или удалены и выдает соответствующий SQL в подключенную базу данных. – Craig W. 4 March 2015 в 19:07
  • 5
    – Gurpreet Singh 10 June 2015 в 10:06

Итак, у вас есть объект, который обновлен, и вы хотите обновить его в базе данных с наименьшим количеством кода ... Параллельность всегда сложна, но я предполагаю, что вы просто хотите, чтобы ваши обновления выиграли. Вот как я сделал это для моего же случая и изменил имена, чтобы имитировать ваши классы. IOW, просто измените attach, чтобы добавить, и он работает для меня:

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();
        }
    }
9
ответ дан Duray Akar 18 August 2018 в 19:58
поделиться

Вы можете использовать метод AddOrUpdate:

db.Books.AddOrUpdate(book); //requires using System.Data.Entity.Migrations;
db.SaveChanges();
38
ответ дан El Mac 18 August 2018 в 19:58
поделиться
  • 1
    ИМО лучшее решение – Norgul 30 November 2016 в 15:16
  • 2
    спас меня часы !!! – Rachmaninoff 11 January 2017 в 14:25
  • 3
    .AddOrUpdate() используется во время миграции базы данных, крайне не рекомендуется использовать этот метод вне миграций, поэтому он находится в пространстве имен Entity.Migrations. – Adam Vincent 5 April 2017 в 01:22
  • 4
    Как сказал @AdamVincent, метод AddOrUpdate() предназначен для миграции, и он не подходит для ситуации, когда вам нужно только обновить существующую строку. В случае, если у вас нет книги с поисковой ссылкой (т.е. идентификатор), она создаст новую строку, и это может быть проблемой в случаях (например, у вас есть API, который должен вернуть вам ответ 404-NotFound, если вы попробуйте вызвать метод PUT для несуществующей строки). – Marko 18 October 2017 в 13:12
  • 5
    Не используйте это, если не знаете, что делаете !!!!!!!!!!!!!!!! читать: michaelgmccarthy.com/2016/08/24/… – Yusha 26 April 2018 в 18:38

Я нашел способ, который отлично работает.

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

        // Mark as Changed
        context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();
0
ответ дан Farhan 18 August 2018 в 19:58
поделиться

Вот лучшее решение для этой проблемы: В представлении добавьте все 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);
}
4
ответ дан H. Pauwelyn 18 August 2018 в 19:58
поделиться

Вы должны использовать. Entry (), если вы хотите обновить все поля вашего объекта. Также имейте в виду, что вы не можете изменить идентификатор поля (key), поэтому сначала задайте 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();
}
4
ответ дан Jarek 18 August 2018 в 19:58
поделиться
  • 1
    Вы должны хотя бы попытаться ответить на вопрос, а не просто опубликовать код – Ivan 17 November 2017 в 01:21
  • 2
    Пожалуйста, сделайте некоторое объяснение этому вопросу вместо того, чтобы просто оставить фрагмент кода, чтобы лучше ответить на вопрос. – alptugd 17 November 2017 в 05:55

Этот код является результатом теста для обновления только набора столбцов без запроса запроса для возврата записи в первую очередь. Сначала он использует код EF 7.

//The function receives an object type to receive a custom object can be a view model or an anonymous object wit the properties you will like 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)
                {
//Here is checking if the property is named Id dont add it in the update. It can be refactored to look in the annotations to find a key or to fin in the name 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 instance of T type object:
            object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

            //loop through the properties of the object you want to covert:          
            foreach (PropertyInfo pi in obj.GetType().GetProperties())
            {
                try
                {
                    //get the value of 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 Cambiar properties a 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 instance of T type object:
            object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

            //loop through the properties of the object you want to covert:          
            foreach (PropertyInfo pi in obj.GetType().GetProperties())
            {
                try
                {
                    //get the value of 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
ответ дан Juan 18 August 2018 в 19:58
поделиться

Попробуйте ....

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
ответ дан Karan 18 August 2018 в 19:58
поделиться

Я просматривал исходный код Entity Framework и нашел способ фактически обновить сущность, если вы знаете свойство Key в другом случае, вам нужно проверить реализацию AddOrUpdate:

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

Hope эта помощь!

85
ответ дан Miguel 18 August 2018 в 19:58
поделиться
  • 1
    Ницца! Нет необходимости перечислять все свойства. Я предполагаю, что вызов SaveChanges() требуется после установки значений. – Jan Zahradník 16 October 2016 в 19:57
  • 2
    Да, изменения будут сохраняться в SaveChanges () – Miguel 17 October 2016 в 20:23
  • 3
    Отличный ответ, было не слишком ясно, что IntelliSense что-то вроде этого не будет работать: _context.MyObj = newObj; затем SaveChanges () или .... _context.MyObj.Update (newObj), затем SaveChanges (); Ваше решение обновляет весь объект без необходимости перебирать все свойства. – Adam 13 October 2017 в 04:17
  • 4
    Это жалуется мне, что я пытаюсь изменить поле ID – Vasily Hall 9 November 2017 в 21:57
  • 5
    Большой!!. Это то, что я искал. – CSharper 30 March 2018 в 18:20
  • 6
    – Gavin Coates 24 August 2018 в 13:07

Как сказал Ренат, удалите: db.Books.Attach(book);

Кроме того, измените ваш запрос результата на использование «AsNoTracking», потому что этот запрос выкидывает состояние модели сущности. Он считает, что «результат» - это книга для отслеживания сейчас, и вы этого не хотите.

var result = db.Books.AsNoTracking().SingleOrDefault(b => b.BookNumber == bookNumber);
1
ответ дан Nez 18 August 2018 в 19:58
поделиться
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();
}
2
ответ дан Nikhil Dinesh 18 August 2018 в 19:58
поделиться

Вы должны удалить db.Books.Attach(book);

3
ответ дан Petter Friberg 18 August 2018 в 19:58
поделиться

Вот мой метод обновления сущности 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() - это крошечная функция полезности, которую я написал задолго до AutoMapper на NuGet:

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);
        }
    }
}
1
ответ дан rasx 18 August 2018 в 19:58
поделиться
0
ответ дан PawelCz 30 October 2018 в 08:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: