假设我有以下实体:
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以外的任何库中签出书籍。
答案 0 :(得分:2)
当您使用.Attach(book)
时,您实际上在告诉EF:嘿,这些是数据库中现有记录的原始值。因此,在您的情况下,EF会认为现有的book.LibraryID
是1
。您可以利用这一事实并强制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
。