2014-01-08 20 views
2

理想情况下,我想有:ServiceStack LINQ的合并域和部分更新

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目标。更新目标中这些属性的值,并将这些更新的属性作为Linq Expression返回以供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); 

回答

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表达式的复杂性死心塌地。您的合并功能,能做到这一点(伪)

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对象e是不同的。

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

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


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

  • 查询整个现有记录的数据库。
  • 反映目标和源对象的属性。
  • 进行值的比较。
  • 构建一个Linq表达式或新对象以跟踪发生了什么变化。
  • 运行更新功能。

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

我希望这会有所帮助。

+1

这是一个非常详细的答案!谢谢,我尝试了解决方案,并告诉你它是如何工作的。 – Tom

+1

@Tom谢谢。希望你得到它的工作。 – Scott