如何使用实体框架更新记录 6.



我正在尝试使用 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: Store更新、插入或删除语句影响了意外数量的行 (0(。实体可能已被修改或删除,因为实体已加载。刷新对象状态管理器条目

您正在尝试更新记录(对我来说,这意味着"更改现有记录上的值并将其保存回来"(。因此,您需要检索对象,进行更改并保存它。

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

我一直在查看实体框架的源代码,如果您知道 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 实现以获取想法。

希望这有帮助!

您可以使用 AddOrUpdate 方法:

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

因此,您有一个已更新的实体,并且您希望在数据库中使用最少的代码更新它...

并发总是很棘手,但我假设您只希望更新获胜。以下是我为同一情况执行此操作并修改名称以模仿您的类的方法。换句话说,只需将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();
    }
}
如果要

更新对象中的所有字段,则应使用 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();
}

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

我找到了一种工作正常的方法。

 var Update = context.UpdateTables.Find(id);
        Update.Title = title;
        // Mark as Changed
        context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();

此代码是仅更新一组列而不进行查询以首先返回记录的测试的结果。它首先使用实体框架 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);
    }
}

对于 .net 核心

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

以下是此问题的最佳解决方案: 在视图中添加所有 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);
}
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();
}

您应该删除db.Books.Attach(book);<</p>

div class="one_answers">

如果对于实体框架 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);
    }
}

这是我的 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);
        }
    }
}

就像 Renat 说的那样,删除: db.Books.Attach(book);

此外,将结果查询更改为使用"AsNoTracking",因为此查询会引发实体框架的模型状态。它认为"结果"是现在要跟踪的书,你不希望这样。

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

试试吧....

更新模型(书(;

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;
        }
    }
}
我知道

它已经被回答了几次,但我喜欢下面的方式。我希望它能帮助某人。

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

最简单的方法就是这样。

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
        {
            // you can't attach book since it doesn't exist in the database yet
            // attach result instead
            db.Books.Attach(result);
            result = book; // this will update all the fields at once
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

尝试使用 Attach(( 然后 SaveChanges(( 组合更新记录时,我遇到了同样的问题,但我使用的是 SQLite DB 及其 EF 提供程序(相同的代码在 SQLServer DB 中工作没有问题(。

我发现,当您的数据库列在SQLite中具有GUID(或UniqueIdentity(并且您的模型是nvarchar时,SQLIte EF默认将其视为二进制(即byte[](。因此,当 SQLite EF 提供程序尝试将 GUID 转换为模型(在我的例子中为字符串(时,它将失败,因为它将转换为 byte[]。解决方法是通过在连接字符串(或元数据,如果首先使用数据库(中定义"BinaryGUID=false;",告诉 SQLite EF 将 GUID 视为文本(因此转换为字符串,而不是字节 [](,如下所示:

  <connectionStrings>
    <add name="Entities" connectionString="metadata=res://savetyping...=System.Data.SQLite.EF6;provider connection string=&quot;data source=C:...db.sqlite3;Version=3;BinaryGUID=false;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

链接到对我有用的解决方案:SQLite 实体框架 6 提供程序如何处理 Guid?

与此特定示例无关,但是在尝试使用 EF 和 DateTime 字段作为并发检查字段时,我遇到了一个挑战。EF 并发代码似乎不遵循元数据 (edmx( 中的精度设置,即 Type="DateTime" Precision="3"。数据库日期时间字段将在字段中存储毫秒分量(即 2020-10-18 15:49:02.123(。即使将实体的原始值设置为包含毫秒分量的日期时间,SQL EF 也会生成如下:

UPDATE [dbo].[People]
SET [dateUpdated] = @0
WHERE (([PeopleID] = @1) AND ([dateUpdated] = @2))
-- @0: '10/19/2020 1:07:00 AM' (Type = DateTime2)
-- @1: '3182' (Type = Int32)
-- @2: '10/19/2020 1:06:10 AM' (Type = DateTime2)

如您所见,@2 是一个没有毫秒分量的 STRING 表示形式。这将导致更新失败。

因此,如果要将 DateTime 字段用作并发键,则必须在检索记录时从数据库字段中删除毫秒/刻度,并且仅使用类似的剥离日期时间传递/更新字段。

    //strip milliseconds due to EF concurrency handling
    PeopleModel p = db.people.Where(x => x.PeopleID = id);
    if (p.dateUpdated.Millisecond > 0)
    {
        DateTime d = new DateTime(p.dateUpdated.Ticks / 10000000 * 10000000);
        object[] b = {p.PeopleID, d};
        int upd = db.Database.ExecuteSqlCommand("Update People set dateUpdated=@p1 where peopleId=@p0", b);
        if (upd == 1)
            p.dateUpdated = d;
        else
            return InternalServerError(new Exception("Unable to update dateUpdated"));
    }
return Ok(p);

当使用新值更新字段时,也去除毫秒

(param)int id, PeopleModel person;
People tbl = db.People.Where(x => x.PeopleID == id).FirstOrDefault();
db.Entry(tbl).OriginalValues["dateUpdated"] = person.dateUpdated;
//strip milliseconds from dateUpdated since EF doesn't preserve them
tbl.dateUpdated = new DateTime(DateTime.Now.Ticks / 10000000 * 10000000);

相关内容

  • 没有找到相关文章

最新更新