我们想要的其实很简单,UPDATE [TABLE] SET ..... WHERE....,尴尬的是这样的语句在LS面前几乎成了奢求,Attach方法勉强可以用用,但是每次重新生成LS实体都要重新设置非条件属性的UpdateCheck = UpdateChek.Never让人无法接受,况且他也就只能做做 WHERE TABLE.XXX = XXX,如果我需要 WHERE TABLE.XXX != XXX或者其他更复杂的条件时这种方法就显得很无力了,扩展,自我扩展,这似乎是我最近在LS娱乐城里玩的最多的游戏了,今天我就要实现这么一个调用Table<TEntity>.Update(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TEntity>> evaluator):
还是得说说JeffreyZhao(他动作太快了,每次我想写的内容一到他博客里看都是成年旧醋了), 他已经做过批更新的,具体可看这里,遗憾的是如他所说:“放弃了对于复杂表达式树的解析,不支持item.Introduction.Length < 10这种条件”,另有BLACK JACK通过正则式匹配查询语句,可看这里,但是我对那个正则表达式不是很放心,毕竟SQL的变化太多。结合了前面两种做法,我也提出我的思路,我们知道IQueryable的查询语句是可以获得的,那么我们能否在批操作时候直接运用这个SQL呢?答案是肯定的,例如我有这么一个SELECT的SQL(OrderId是主键):
SELECT * from Order WHERE Order.OrderId = 123;
那么可以想办法更新这些查询结果中的记录,SQL语句可以这么写:
UPDATE [Order]
SET [XXX] = xxxx
FROM [dbo].[Order ] AS T1 INNER JOIN (
SELECT *
FROM [Order]
WHERE [Order].[OrderId ] = 123
) AS T2 ON (T1.[OrderId] = T2.[OrderId])
按照上面的举例,直接调用上面的GetFormatCmdText扩展方法我们将得到如下SQL:
UPDATE [Order]
SET {0}
FROM [dbo].[Order ] AS T1 INNER JOIN (
SELECT *
FROM [Order]
WHERE [Order].[OrderId ] = 123
) AS T2 ON (T1.[OrderId] = T2.[OrderId])
http://blogs.msdn.com/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
里面有ExpressionVisitor类可供使用,代码我就不贴出来了,需要的朋友可浏览上面的链接。通过代码我们可以获取赋值表达式的MemberInitExpression对象,把它转换成SET的SQL语句片段也很简单:
需要注意的是,上面的代码参数中的updateCommand,由于我们的updateCommand其实是通过IQueryable获得的,所以里面可能带了参数,因此我们赋值用的参数必须向这个Command中添加,最后就是语句拼接和参数处理了,也就是完成扩展方法:
有了这个扩展我们就可以这样更新数据了:
这个扩展并非完美无暇,可能有朋友注意了上面代码中GetDbSetString方法有这么一句:
else
{
throw NotSupportedException("not support the method call expression");
}
经发现有时候Expression<Func<TEntity, TEntity>> evaluator表达式的可能产生内嵌的MethodCallExpression的,对于这样的表达式我没有更好的方式来解析,因此放弃了这种情况的处理,例如:
o=> new Order() {Number= o.Number.Number+ " AAA"}
对于这种内联调用这个扩展暂时无能为力,好了,文章完,有了UPDATE的思路DELETE也就可以同样的方式扩展了。