我正在ASP.NET MVC4和EF5上的网站上工作。我想确保在数据库中仅更新修改后的值。我正在使用一个工作模式单元和一个存储库来进行数据工作。这是通用存储库的代码。我有两个问题,需要对以下代码进行代码审查。


仅更新修改后的字段的代码好吗?它有效,但是我不知道这是否是最好的方法。我特别不喜欢我必须自己遍历属性的循环(EF不能这样做吗?)。我也不喜欢强制转换为对象和进行比较。
我的Add和Update方法怎么样?我正在计划客户端(MVC / API控制器)使用服务(传递工作单元)来完成工作。他们不会直接使用存储库。该存储库将没有Save()方法,只有UnitOfWork将有一个。我可以确保在服务中使用相同的上下文来更新/添加实体。鉴于此,在任何情况下我的添加/更新代码都会失败吗?

public class EFRepository<T> : IRepository<T> where T : class
{
    protected DbContext DbContext { get; set; }
    protected DbSet<T> DbSet { get; set; }

    public EFRepository(DbContext dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        DbContext = dbContext;
        DbSet = DbContext.Set<T>();
    }

    public virtual void Add(T entity)
    {
        DbContext.Entry(entity).State = EntityState.Added;
    }

    public virtual void Update(T entity)
    {
        //dbEntityEntry.State = EntityState.Modified; --- I cannot do this.

        //Ensure only modified fields are updated.
        var dbEntityEntry = DbContext.Entry(entity);
        foreach (var property in dbEntityEntry.OriginalValues.PropertyNames)
        {
            var original = dbEntityEntry.OriginalValues.GetValue<object>(property);
            var current = dbEntityEntry.CurrentValues.GetValue<object>(property);
            if (original != null && !original.Equals(current))
                dbEntityEntry.Property(property).IsModified = true;
        }
    }

    //... Other methods ...
}


评论

因此,这是假设T实体已经不是直接从dbcontext直接获取的对象吗?

是的,T不是dbcontext.Person's.First()之类的东西。 T是Ef自动生成的域类。通常从视图模型创建此类型的实例。

有什么理由为什么您不能直接获得实体并且只能操纵它?那么您不必担心调用任何更新方法。也许您可以使用诸如AutoMapper之类的方法从您的视图模型映射到模型?只是想法

如果要从上下文中获取对象,则可以通过修改这些属性,然后在上下文中调用SaveChanges来对模型进行更新。 EF会为您处理。是您的理解吗?

只是更新,我完全摆脱了EF,现在完全使用Dapper。

#1 楼

为什么要检查值/属性是否已更改?您只是无缘无故地增加了额外的复杂性。当然可以,可能略有!更快以仅更新修改后的值,但是作为回报,您增加了开销以跟踪修改后的值,这将减少您获得的任何性能提升。

只需在整个对象上调用Update ...所有未更改的值将保持不变,而已修改的值将更新。

如果速度很快,那么您很可能在寻找错误的位置来挤压ms。

评论


\ $ \ begingroup \ $
我从数据库的角度考虑。如果我连续更新所有字段,那么该表中的所有索引都会更新。好的,很可能我不会对所有实体都具有这种逻辑,但是在某些大型表中,我需要具有此选项。
\ $ \ endgroup \ $
– Narayana
2013年12月13日14:09

\ $ \ begingroup \ $
@Narayana,因为C#社区(注:不是C#本身)的很大一部分对任何敢于在性能方面进行思考的人都会造成惩罚。这是一个不,不。是的,你是对的,对此我有点不高兴。
\ $ \ endgroup \ $
–尼古拉斯·彼得森(Nicholas Petersen)
16年4月4日在18:29

\ $ \ begingroup \ $
如果经过所有检查后发现根本不需要更新,我们可以将整个往返行程保存到数据库中,该怎么办?更不用说潜在的缓存失效了,就像@Narayana提到重建索引一样。
\ $ \ endgroup \ $
–史蒂夫
17年4月16日在17:39

\ $ \ begingroup \ $
要扩展无需更改即可更新数据库中字段的想法,请考虑(尽管写得不好)触发器,该触发器检查IF UPDATE(FieldName)并执行逻辑。如果EF在update语句中包括未更改的字段,它将在IF UPDATE(FieldName)检查中检查true,并可能执行不必要的逻辑。
\ $ \ endgroup \ $
–克里斯·波特(Chris Porter)
17年4月19日在20:46

\ $ \ begingroup \ $
这是一个很糟糕的答案..如果您正在开发移动应用程序并且表很大。只需将整个DTO发送回所有45列并更新索引?如上所述,如果表已被大量索引该怎么办?现在我们也应该在10个索引中引起碎片吗?不敢相信这获得了24票
\ $ \ endgroup \ $
–汤姆·德米勒
19年8月6日在13:48



#2 楼

如果您担心update语句可能会使查询速度变慢,那么我建议您使用一种稍微不同的方法。您可以修改代码以传递修改后的属性列表。这样,在您知道哪个属性被更新的情况下,您可以选择使其更快,并且当不提供任何属性时,则可以全部更新-

>这种方法可让您自由执行以下两项功能-

>还要注意,我正在服用params。因此,您可以随意链接多个属性。

评论


\ $ \ begingroup \ $
我很欣赏这个想法,因为它非常有用,但是我很难理解如何实现“ else”子句来查找更新的条目。每当我的OriginalValues与每个属性的CurrentValues匹配时。如何知道原始值?
\ $ \ endgroup \ $
–乔什
17年2月6日在20:25

#3 楼

我只是在学习EF,但我担心并发用户进行更新。

调用SaveChanges时,最好是EF仅更新已更改的字段,以最大程度地减少发生冲突或覆盖并发用户更改的机会。

评论


\ $ \ begingroup \ $
嗨,我最终在上下文中完成了所有工作,因此EF负责了。我现在没有断开连接的场景,这意味着,我没有明确设置修改或添加状态。我让EF来照顾那些人。
\ $ \ endgroup \ $
– Narayana
14年2月18日在11:36

\ $ \ begingroup \ $
因此,如果您对实体使用更改跟踪,我认为EF只会为那些脏的列生成更新。但我知道也可以绕过或覆盖它(例如,在设置字段值之后附加),因此只想确保每个人的代码都注意这一点。
\ $ \ endgroup \ $
–sdmcnitt
2014年2月20日在20:32



\ $ \ begingroup \ $
@sdmcnitt,那么这可能应该是评论...
\ $ \ endgroup \ $
–Vogel612♦
2014-2-27在18:05