ServiceStack Linq合并字段和部分更新

时间:2014-01-08 07:56:18

标签: c# linq servicestack ormlite-servicestack

理想情况下,我希望:

   public user Update(User dto) {
        var user = userRepository.GetUserById(dto.Id);
        var mergedFields = Merge(user, dto); //my dream function
        userRepository.UpdateOnly(user, mergedFields)
                      .Where(u => u.Id == user.Id); //OrmLite UpdateOnly func
        return user;
    }

Merge是我的deam函数返回Linq表达式:

Expression<Func<T, TKey>> Merge(T target, T source)

因此,Merge知道从T源更新到T目标的内容。更新target中这些属性的值,并将这些更新的属性作为Linq Expression for OrmLite UpdateOnly返回使用。

然而,我正在拉我的头发,我无法弄清楚如何编写这个Merge函数。请给我一些帮助!

谢谢!


参考:ServiceStack OrmLite是light weight ORM。它的UpdateOnly函数采用这样的Linq表达式:

.UpdateOnly(new User {FirstName="admin", LastName="my", OtherStuff="etc..."},
            u => {u.FirstName, u.LastName}).Where(u => u.Id == 123);

1 个答案:

答案 0 :(得分:3)

虽然我可以看到你想要做什么,但已经有一个内置机制来实现部分更新,而不必构建更改值的Linq表达式。

我认为OrmLite的UpdateNonDefaults更适合您的任务。

您的更新操作应该只接收对DTO中现有记录的更改,而不是完整对象。所以这样做应该足够了:

db.UpdateNonDefaults(dto, u => u.Id == 123);

SQL中的结果:

UPDATE "User" SET "FirstName" = 'admin', "LastName" = 'my' WHERE ("UserId" = 123);

如果您的更新请求其中包含完整对象,则数据库只会覆盖具有相同值的所有现有值,但此操作的成本不应超过查找时间的处理时间。整个现有对象,进行比较以使用反射确定更改,构建Linq表达式并运行UpdateOnly查询。


如果您已经设置了检查更改字段与原始字段,那么您可以在没有Linq表达式的复杂性的情况下执行此操作。您的合并功能可以执行此操作(PseudoCode)

public T Merge(T target, T source)
{
  1. 为退货创建新的默认对象。即var result = default(T);
  2. 反映您的T target公共媒体资源:

    foreach(var property in target.GetType().GetPublicProperties()){
    

    每个反射属性:

    1. 使用EqualityComparer确定值是否已更改: if(!EqualityComparer<FieldType>.Default.Equals(targetField, sourceField))

    2. 如果值不同,请在result对象上设置值。

  3. 返回result个对象。它只会有变化。

  4. 现在使用UpdateNonDefaults结果将确保只有更改包含在更新SQL中。


    检查更改的字段是否值得?您应该运行一些基准测试。请记住,检查涉及:

    • 查询整个现有记录的数据库。
    • 反映目标和源对象的属性。
    • 比较价值观。
    • 构建Linq表达式或新对象以跟踪已更改的内容。
    • 运行更新功能。

    如果您在确定对象的更改时遇到困难,the answers in this question可能会有所帮助。

    我希望这会有所帮助。