我正在使用Entity Framework Code First,虽然我有工作代码,但我必须制作严格不必要的数据库调用才能处理以下更新。
我有一个简单的POCO课程,用于包含相关标签集合的相册:
public class Album
{
public int Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public virtual IList<Tag> Tags { get; private set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
}
这是通过MVC表单更新的 - 标签由一系列复选框表示。
因此,当我在存储库中访问我的Update方法时,我有一个填充了标签列表的专辑类 - 理论上我只需要进行更新。
然而,我能找到的唯一方法是获取要更新的标签列表(删除以前设置但现在未选中的任何标签,并添加当前已检查的任何标签)是从上下文中检索原始相册更新它。
其次,因为在我的实现中,Tag的Name字段标有[Required],而在我的Album对象中填充的表单中我只有标签的ID,我还必须在更新前检索每个标签
这是我的代码:
public void Update(Album album)
{
var albumToUpdate = GetById(album.Id); // - need to retrieve album with tags in order to update tags
albumToUpdate.Title = album.Title;
albumToUpdate.Price = album.Price;
albumToUpdate.Tags.Clear();
if (album.Tags != null)
{
foreach (var tag in album.Tags)
{
var tagToAdd = context.Tags.Find(tag.Id); // - need to retrieve full details of tag so doesn't fail validation
albumToUpdate.AddTag(tagToAdd);
}
}
}
感谢任何有关如何通过减少数据库命中来适应这一点的想法。对于这个特定的功能(网站管理工具的一部分)来说,这不是一个主要的交易,但我想知道我做得最好的方式。
答案 0 :(得分:0)
首先,我认为这种更新模式在某种程度上是错误的,而不是传入一个我认为是你想要更新的副本或部分副本的相册(至少相同的ID),为什么不你先加载实际的并将更改应用到它?
如果你不能这样做,可能不会混淆不传入同一个实体(相册),而是使用数据传输对象(DTO)或其他只包含您需要的字段的消息,然后将其应用于加载相册。
至于如何避免加载每个标签的主要问题,EF应该为你做,但我不知道它。例如,如果您只设置关系,NHibernate将不会加载延迟实体,因为您没有触及Tag的任何属性,因此它只需要Id来使用它。希望EF做同样的事情,但也许不行(我假设你已经对它进行了描述)。
如果EF不像那样,你可以尝试两件事:首先,只要Tag上没有级联更新,使用只有ID的骨架(即自己创建对象并只设置Id );如果EF级联更新标记,这将无效。其次,您可以为Tags实现自己的缓存并从内存中获取它们。
答案 1 :(得分:0)
您的方法 - 从数据库重新加载实体图并手动将更改合并到其中 - 在我看来是正确的,您可以做到最好。
忘了你使用Entity Framework的时刻。如果必须手动编写SQL语句,您会怎么做? (EF是SQL语句生成器的包装器。)您将回发一个对象图 - 一个Album
,其列表为Tags
。你现在如何决定用哪个标签写一个INSERT,它标记一个DELETE,哪个标记一个UPDATE语句? (我假设你在Album
和Tag
之间的关系是多对多的,所以你写入一个连接表。)如果你不知道数据库中的原始状态你不能决定。标签关系是否存在于数据库中?无论是使用EF还是直接SQL,都必须查询数据库以找到答案。
我只看到两种选择:
跟踪实体自行更改。对于您的MVC Web应用程序,这意味着您必须将原始状态与前GET请求存储在某处,例如在会话状态或页面中的隐藏输入字段中。使用POST请求,您可以检索原始状态,构建并附加原始图并将更改合并到其中。
编写一个存储过程,该过程接受相册和标签集合,让SP完成工作以创建相应的SQL语句。
第一种方法很复杂,在HTTP有效负载(隐藏输入字段)中有成本,或者取决于脆弱的会话状态。第二个与您使用ORM的原因冲突。除非您有严重的性能问题或者是SQL主服务器,否则我不会考虑存储过程。