如何添加条件以使用实体框架linq-to-entities更新条目

时间:2016-10-14 17:26:12

标签: c# entity-framework

假设我有以下实体:

public class Library
{
    public int ID { get; set; }

    public ICollection<Book> Books { get; set; }

    public Library()
    {
        Books = new HashSet<Book>();
    }
}
public class Book
{
    public int ID { get; set; }
    public int LibraryID { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public DateTime PublicationDate { get; set; }
    public bool CheckedOut { get; set; }
}

有很多库,每个库都有一堆书。 ID = 1的库中的某人检查ID = 42的书,所以我想将CheckedOut属性更新为true。假设无论出于何种原因我已经知道了库ID和书籍ID,而不知道其他信息。我可以非常轻松地更新CheckedOut属性,而无需从数据库中获取所有书籍数据:

Book book=new Book(){
    ID=42,
    LibraryID=1,
    CheckedOut=true
}
context.Books.Attach(book);
var entry=context.Entry(book);
entry.Property(b=>b.CheckedOut).IsModified=true;
context.SaveChanges();

这是我的问题。如果数据库中的LibraryID不是本书的1,我怎么能强制它失败?在简单的SQL中我可以写

UPDATE Books SET CheckedOut=1 WHERE ID=42 AND LibraryID=1

如何使用实体框架做同样的事情?

这里一个明显的用例是添加更多安全性 - 例如,如果用户没有权限从1以外的任何库中签出书籍。

1 个答案:

答案 0 :(得分:2)

当您使用.Attach(book)时,您实际上在告诉EF:嘿,这些是数据库中现有记录的原始值。因此,在您的情况下,EF会认为现有的book.LibraryID1。您可以利用这一事实并强制EF执行所需的检查,方法是通过数据注释将LibraryID属性配置为Optimistic Concurrency Token

public class Book
{
    // ...
    [ConcurrencyCheck]
    public int LibraryID { get; set; }
}

或Fluent API:

modelBuilder.Entity<Book>()
    .Property(e => e.LibraryID).IsConcurrencyToken();

现在,如果你打开EF登录并执行你的代码片段,你会看到如下内容:

于2016年10月14日21:35:32 +03:00

打开连接
UPDATE [dbo].[Books]
SET [CheckedOut] = @0
WHERE (([ID] = @1) AND ([LibraryID] = @2))

-- @0: 'True' (Type = Boolean)

-- @1: '42' (Type = Int32)

-- @2: '1' (Type = Int32)

与你所追求的类似。

此方法的唯一潜在问题是,当数据库中的DbUpdateConcurrencyException不是LibraryID时,您将获得1