
置顶随笔
摘要: 前言 本来开篇应该介绍什么是 Expression Tree,以及为什么需要 Expression Tree,或者.Net4 关于 Expression Tree 的改进等等,但其实我对于Expression Tree的理解也是来自于google,你不妨 google一下 获取更多更新的介绍。 目录 Expression Tree 值不值得我去学? .Net4 Expression Tree ...
阅读全文
posted @ 2010-05-17 00:39 CoolCode 阅读(1178) | 评论(4) |
编辑

2010年7月26日
jstree
本文基于 jsTree 1.0-rc1 版本增加节点的双击事件。
jsTree 是基于jquery的树插件,支持拖放、复制、删除、快捷键、多选、自定义节点图标、自定义右键菜单、跨页面保存状态等等,总之我想到的它基本上都有了,而且最值得表扬的是它让人感觉一点都不慢哦。
jsTree有节点选择事件,即
.bind("select_node.jstree", function(e, data) {
//alert(data.rslt.obj.attr("id") + ":" + data.rslt.obj.attr("rel"));
})
其实我认为它更像是节点的单击事件,因为每次点节点的时候它都会触发,不管之前该节点是否已经被选中。
近日做个文件管理的东东,需要用到节点的双击事件,如双击某个节点打开该节点的编辑页面。
jstree虽然有双击事件,但是并非针对节点的,而是你双击树所在区域就会触发,如上图任何一个地方。
离节点双击事件最接近的应该就是节点选择事件,因此又是“照葫芦画瓢”啦。
分析
在第833行 this.get_container() 后是节点的单击事件
.delegate("a", "click.jstree", $.proxy(function (event) {
event.preventDefault();
this.select_node(event.currentTarget, true, event);
}, this))
同样我再这里插入节点双击事件
.delegate("a", "dblclick.jstree", $.proxy(function(event) {
event.preventDefault();
this.dblclick_node(event.currentTarget, true, event);
}, this))
接着,我再实现 dblclick_node 方法就可以了。
在第928行找到 select_node 的代码,比较复杂。但里面90%对于双击来说是没有用处的,如处理单选、多选、保存选择结果到cookies等。因此 dblclick_node 方法的实现要比 select_node 简单很多。
dblclick_node: function(obj, check, e) {
obj = this._get_node(obj);
if (obj == -1 || !obj || !obj.length) { return false; }
this.__callback({ "obj": obj });
},
OK,就这样了。
使用例子
跟 select_node 用法一样
.bind("dblclick_node.jstree", function(e, data) {
//alert(data.rslt.obj.attr("id") + ":" + data.rslt.obj.attr("rel"));
})
改造后的代码下载
jquery.jstree.js
顺便说说
jstree 跟另一个插件jquery validate 是水火不容的,当两者共存时,jstree虽然也可以构造树出来,但如僵尸一般不能展开。这里mark一个,日后试试能否修改。
posted @ 2010-07-26 19:59 CoolCode 阅读(149) | 评论(2) |
编辑

2010年7月18日
传说中的dynamic
dynamic是个不合群、不按规则办事的家伙,可以说是个异形,但更恐怖的是它又是无所不知的,任何事情都难不了它(咳咳,它似乎与Lambda表达式是死对头)。这令人想起《死亡日记》的怪异侦探L,行为怪异而智力超人,以至于离奇的案件不得不交给了他。dynamic可以看成是一切类型的化身,但并不是仅限于此,它像《未来战士》续集里面的T-1000型液体金属的终结者。噢~~~~似乎扯的有点远了
饱经风雨而不倒的ADO.NET
ADO.NET 从来做事都有理有据,而且又异常专注于自身领域,是个professional的牛人,令人想起《美丽心灵》里面的博弈论和微分几何学领域潜心研究以致获得诺贝尔经济学奖的数学家—— 约翰·福布斯·纳什 教授(咳咳,纳什教授是个妄想型精神分裂的~~~嗯,这个以后再说)。
关于ADO.NET 的例子
1. 执行SQL语句
using (DbCommand command = connection.CreateCommand())
{
command.CommandText = "select Top 10 * from Orders";
command.CommandType = CommandType.Text;
using (IDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("OrderID: {0}, OrderDate: {1}",
reader.GetInt32(reader.GetOrdinal("OrderID")),
reader.GetDateTime(reader.GetOrdinal("OrderDate")));
}
}
}
2. 调用存储过程
command.CommandText = "CustOrdersOrders";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("CustomerID", "ALFKI"));
//略去...
当 ADO.NET 遇上 dynamic
某年某月ADO.NET不幸遇到dynamic,从此循规蹈矩的生活不复存在。dynamic说它可以帮助 ADO.NET 丢掉 DataSet 的包袱,而且在不用创建数据实体的情况下,实现查询结果垮不同方法传递;更加强大的地方是可以与存储过程无缝连接,即像调用一般方法一样调用存储过程而不用写额外代码。我的神哪~~~ ADO.NET 听了dynamic一番游说后,心底下不禁惊讶一下。dynamic又说,实现刚才所说的工程只要借你手下的两大猛将 SqlConnection 和 SqlCommand 助我一臂之力即可。
dynamic真有如此奇技? ADO.NET 虽有怀疑,但它想到曾经看过一部叫《阿甘正传》的电影,里面的阿甘虽然是弱智人,但参军时练就乒乓奇技,后来还和中国国手同台竞技。想到这,ADO.NET 认为不能因为对方弱智就不相信对方的话,这是很不礼貌很不绅士的人才会做的事,所以它相信了dynamic。
dynamic 果真不负众望,三两脚猫功夫就交出成果了。
dynamic重构后的数据库操作
using (dynamic command = connection.CreateDynamicCommand())
{
//执行查询SQL
IEnumerable<dynamic> toptenOrders = command("select Top 10 * from Orders");
foreach (dynamic order in toptenOrders)
{
Console.WriteLine("OrderID: {0}, OrderDate: {1}", order.OrderID, order.OrderDate);
}
//执行带参数的SQL
IEnumerable<dynamic> customerOrders = command("select * from Orders where CustomerID = @CustomerID",
CustomerID: "ALFKI");
foreach (dynamic order in customerOrders)
{
Console.WriteLine("OrderID: {0}, OrderDate: {1}", order.OrderID, order.OrderDate);
}
//调用存储过程
IEnumerable<dynamic> orders = command.CustOrdersOrders(CustomerID: "ALFKI");
foreach (dynamic order in orders)
{
Console.WriteLine("OrderID: {0}, OrderDate: {1}", order.OrderID, order.OrderDate);
}
}
要知道 ADO.NET 可不是.NET菜鸟,它看到 command("select Top 10 * from Orders"); 第一感觉认为吃了dynamic药的command有可能是委托类型,而看到后面的 command.CustOrdersOrders(CustomerID: "ALFKI"); 不得不否决了前面的看法。dynamic到底是什么东西?可以这样认为,dynamic什么东西都是;也可以认为,dynamic不是什么东西!
ADO.NET 知道任何.NET写的再高深的代码在reflector下都会现出原形,通过对 command 解剖,立刻明白原来自己跟《美丽心灵》的纳什教授一样纠缠于一种不存在的幻想不能自拔,reflector告诉我们:dynamic实际上是不存在的!
还是鲁迅叔叔说的好,世界上本没有dynamic,只是微软对委托封装得太牛了,也便有了dynamic。
结语
聪明的你知道command是怎么实现了吗?不妨先想想,然后展开下面的代码看看是否与你想的一致。
点此展开代码
注:本文存储过程部分参考了微型ORM.
public static class Extensions
{
public static DynamicCommand CreateDynamicCommand(this DbConnection connection)
{
return new DynamicCommand(connection);
}
}
/// <summary>
/// 动态Command
/// </summary>
public class DynamicCommand : DynamicObject, IDisposable
{
public DbConnection Connection { get; set; }
public DynamicCommand(DbConnection connection)
{
this.Connection = connection;
}
//实现SQL语句查询
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
if (args.Length == 0) throw new ArgumentException("args must has value");
result = Execute(args[0].ToString(), CommandType.Text, binder.CallInfo.ArgumentNames, args.Skip(1).ToArray());
return true;
}
//实现存储过程
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.CallInfo.ArgumentNames.Count != binder.CallInfo.ArgumentCount)
{
throw new ArgumentException("All parameters must be named");
}
result = Execute(binder.Name, CommandType.StoredProcedure, binder.CallInfo.ArgumentNames, args);
return true;
}
/// <summary>
/// 执行SQL查询
/// </summary>
/// <param name="commandText"></param>
/// <param name="commandType"></param>
/// <param name="names"></param>
/// <param name="args"></param>
/// <returns></returns>
private object Execute(string commandText, CommandType commandType, IEnumerable<string> names, object[] args)
{
bool manageConnectionLifespan = (this.Connection.State == ConnectionState.Closed);
if (manageConnectionLifespan) this.Connection.Open();
try
{
using (var cmd = this.Connection.CreateCommand())
{
cmd.CommandType = commandType;
cmd.CommandText = commandText;
for (int i = 0; i < args.Length; i++)
{
DbParameter param = cmd.CreateParameter();
param.ParameterName = "@" + names.ElementAt(i);
param.Value = args[i] == null ? DBNull.Value : args[i];
cmd.Parameters.Add(param);
}
return ExecuteList(cmd);
}
}
finally
{
if (manageConnectionLifespan)
{
this.Connection.Close();
}
}
}
/// <summary>
/// 执行SQL命令,返回查询结果列表
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
private static IEnumerable<dynamic> ExecuteList(DbCommand command)
{
List<DynamicEntity> resultList = new List<DynamicEntity>();
using (DbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
DynamicEntity entity = new DynamicEntity();
for (int i = 0; i < reader.FieldCount; i++)
{
entity.SetMember(reader.GetName(i), reader.GetValue(i));
}
resultList.Add(entity);
}
}
return resultList;
}
}
/// <summary>
/// 动态实体
/// </summary>
internal class DynamicEntity : DynamicObject
{
/// <summary>
/// 属性和值的字典表
/// </summary>
private Dictionary<string, object> values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (values.ContainsKey(binder.Name))
{
result = values[binder.Name];
}
else
{
throw new System.MissingMemberException("The property " + binder.Name + " does not exist");
}
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
SetMember(binder.Name, value);
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return values.Keys;
}
internal void SetMember(string propertyName, object value)
{
if (object.ReferenceEquals(value, DBNull.Value))
{
values[propertyName] = null;
}
else
{
values[propertyName] = value;
}
}
}
posted @ 2010-07-18 17:46 CoolCode 阅读(2132) | 评论(29) |
编辑

2010年7月11日
方法一、官方例子
地球人都知道的,也是不少 Linq To SQL 反对者认为效率低下的一种方法。
NorthwindDataContext db = new NorthwindDataContext();
var customers = db.Customers.Where(c => c.CustomerID.StartsWith("BL"));
foreach (var customer in customers)
{
customer.Address = "Guangzhou";
customer.ContactName = "CoolCode";
customer.CompanyName = "Microsoft";
}
db.SubmitChanges();
这种方法必须要查询出要更新的数据,确实有点不雅,也是Linq To SQL 略显尴尬的一面。
方法二、使用ExpressionVisitor获取Lambda表达式生成的SQL条件语句
此方法是基于Jeffrey Zhao 的《扩展LINQ to SQL:使用Lambda Expression批量删除数据》,从该文章得到一点启发,继而有了批量更新。使用示例:
db.Customers.Update(c => c.CustomerID == "Bruce",
c => new Customer
{
Address = "Guangzhou",
ContactName = "CoolCode",
CompanyName = "Microsoft"
});
方法原型:
/// <summary>
/// 批量更新
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="table">表</param>
/// <param name="predicate">查询条件表达式</param>
/// <param name="updater">更新表达式</param>
/// <returns>影响的行数</returns>
public static int Update<T>(this Table<T> table, Expression<Func<T, bool>> predicate, Expression<Func<T,T>> updater) where T : class
实现原理:扩展Table<T>,解释表达式树成SQL语句。其中解释表达式树包括和更新表达式,后者相对容易处理,例如表达式:
c => new Customer { Address = "Guangzhou", ContactName = "CoolCode", CompanyName = "Microsoft" }
解释成
Address = @Address, ContactName = @ContactName, CompanyName = @CompanyName
而相应的值("Guangzhou", "CoolCode", "Microsoft" )作为SQL参数传递。
实现这一步,其实就是从表达式 Expression<Func<T,T>> 中取到初始化的属性名字和值就可以,具体做法可以使用Expression Tree Viewer来辅助,从下图可以了解到 Expression<Func<T,T>> 的树形结构。
然后我按上面的结构图“照葫芦画瓢”就得到要更新的属性名字和值:
//获取Update的赋值语句
var updateMemberExpr = (MemberInitExpression)updater.Body;
var updateMemberCollection = updateMemberExpr.Bindings.Cast<MemberAssignment>().Select(c => new
{
Name = c.Member.Name,
Value = ((ConstantExpression)c.Expression).Value
});
而解释where条件就相对没这么轻松了。
这里同 Jeffrey Zhao 的批量删除一样,同样是借助 ExpressionVisitor 来解释。ExpressionVisitor 是 Expression Tree 的遍历器,它自身不会帮你生成任何东西,通过继承 ExpressionVisitor 就可以取表达式的任何信息,本文就是通过让 ConditionBuilder 继承ExpressionVisitor 而生成 Where 条件的 SQL。
注:Jeffrey Zhao 的批量删除一文提供的源代码中,ConditionBuilder 并不支持生成Like操作,如 字符串的 StartsWith,Contains,EndsWith 并不能生成这样的SQL: Like ‘xxx%’, Like ‘%xxx%’ , Like ‘%xxx’ 。我通过分析 ExpressionVisitor ,也不难发现只要override VisitMethodCall 这个方法即可实现上述功能。
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m == null) return m;
string format;
switch (m.Method.Name)
{
case "StartsWith":
format = "({0} LIKE {1}+'%')";
break;
case "Contains":
format = "({0} LIKE '%'+{1}+'%')";
break;
case "EndsWith":
format = "({0} LIKE '%'+{1})";
break;
default:
throw new NotSupportedException(m.NodeType + " is not supported!");
}
this.Visit(m.Object);
this.Visit(m.Arguments[0]);
string right = this.m_conditionParts.Pop();
string left = this.m_conditionParts.Pop();
this.m_conditionParts.Push(String.Format(format, left, right));
return m;
}
到此刻,已经解决了解释表达式树的难题,那么实现通过表达式树生成完整的 Update SQL语句这个设想也不是什么难事了。
/// <summary>
/// 批量更新
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="table">表</param>
/// <param name="predicate">查询条件表达式</param>
/// <param name="updater">更新表达式</param>
/// <returns>影响的行数</returns>
public static int Update<T>(this Table<T> table, Expression<Func<T, bool>> predicate, Expression<Func<T,T>> updater) where T : class
{
//获取表名
string tableName = table.Context.Mapping.GetTable(typeof(T)).TableName;
//查询条件表达式转换成SQL的条件语句
ConditionBuilder builder = new ConditionBuilder();
builder.Build(predicate.Body);
string sqlCondition = builder.Condition;
//获取Update的赋值语句
var updateMemberExpr = (MemberInitExpression)updater.Body;
var updateMemberCollection = updateMemberExpr.Bindings.Cast<MemberAssignment>().Select(c => new
{
Name = c.Member.Name,
Value = ((ConstantExpression)c.Expression).Value
});
int i = builder.Arguments.Length;
string sqlUpdateBlock = string.Join(", ", updateMemberCollection.Select(c => string.Format("[{0}]={1}", c.Name, "{" + (i++) + "}")).ToArray());
//SQL命令
string commandText = string.Format("UPDATE {0} SET {1} WHERE {2}", tableName, sqlUpdateBlock, sqlCondition);
//获取SQL参数数组 (包括查询参数和赋值参数)
var args = builder.Arguments.Union(updateMemberCollection.Select(c => c.Value)).ToArray();
//执行
return table.Context.ExecuteCommand(commandText, args);
}
例如上面提到的示例所生成的 Updae SQL语句是:
UPDATE dbo.Customers SET [Address]={1}, [ContactName]={2}, [CompanyName]={3} WHERE ([CustomerID] = {0})
相应参数:"Bruce", "Guangzhou", "CoolCode", "Microsoft"
据不完全统计,实际开发中用的 Update SQL 90%是很简单的,以上扩展基本上符合要求。
方法三、使用 LinqToSQL 自身的解析器来获取Lambda表达式生成的SQL条件语句
该方法与方法二基本上是同一思路,只是在获取Lambda表达式生成的SQL条件上有点不一样。
通过 DataContext 的 GetCommand 可以获取到 DbCommand,所以通过生成的SQL查询语句中截取Where后面的条件,再用方法二生成Update 的赋值语句,两者拼凑起来即可。
该方法比方法二支持更多Lambda表达式(实际上就是所有LinqToSQL支持的)生成SQL条件。
/// <summary>
/// 批量更新
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="table">表</param>
/// <param name="predicate">查询条件表达式</param>
/// <param name="updater">更新表达式</param>
/// <returns>影响的行数</returns>
public static int Update<T>(this Table<T> table, Expression<Func<T, bool>> predicate, Expression<Func<T,T>> updater) where T : class
{
//获取表名
string tableName = table.Context.Mapping.GetTable(typeof(T)).TableName;
DbCommand command = table.Context.GetCommand(table.Where(predicate));
string sqlCondition = command.CommandText;
sqlCondition = sqlCondition.Substring(sqlCondition.LastIndexOf("WHERE ", StringComparison.InvariantCultureIgnoreCase) + 6);
//获取Update的赋值语句
var updateMemberExpr = (MemberInitExpression)updater.Body;
var updateMemberCollection = updateMemberExpr.Bindings.Cast<MemberAssignment>().Select(c =>
{
var p = command.CreateParameter();
p.ParameterName = c.Member.Name;
p.Value = ((ConstantExpression)c.Expression).Value;
return p;
})
.ToArray();
string sqlUpdateBlock = string.Join(", ", updateMemberCollection.Select(c => string.Format("[{0}]=@{0}", c.ParameterName)).ToArray());
//SQL命令
string commandText = string.Format("UPDATE {0} SET {1} FROM {0} AS t0 WHERE {2}", tableName, sqlUpdateBlock, sqlCondition);
//获取SQL参数数组 (包括查询参数和赋值参数)
command.Parameters.AddRange(updateMemberCollection);
command.CommandText = commandText;
//执行
try
{
if (command.Connection.State != ConnectionState.Open)
{
command.Connection.Open();
}
return command.ExecuteNonQuery();
}
finally
{
command.Connection.Close();
command.Dispose();
}
}
同样使用文章开头的示例,生成的 Update SQL 跟方法二略有不同:
UPDATE dbo.Customers SET [Address]=@Address, [ContactName]=@ContactName, [CompanyName]=@CompanyName FROM dbo.Customers AS t0 WHERE [t0].[CustomerID] = @p0
方法四、支持多表关联的复杂条件
要知道,前面提到的方法二和三都不支持多表关联的复杂条件。可以用一个示例让大家更清楚为什么——
例如,更新CustomerID=“Bruce”的用户的所有订单的送货日前是一个月后。
db.Orders.Update(c => c.Customer.CustomerID == "Bruce",
c => new Order
{
ShippedDate = DateTime.Now.AddMonths(1)
});
应该生成的 Update SQL 语句是:
UPDATE [dbo].[Orders] SET [ShippedDate] = @p1
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
WHERE [t1].[CustomerID] = @p0
--@p0 = 'Bruce', @p1 = '2010-08-11'
但遗憾的是无论用方法二或三都会抛异常,因为两者皆没法解释多表关联生成的语句: “LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID] ”
一位叫 Terry Aney 的朋友在《Batch Updates and Deletes with LINQ to SQL》这篇博文中解决了这个问题。使用他提供的UpdateBatch 方法生成的 Update SQL 是:
UPDATE [dbo].[Orders]
SET [ShippedDate] = @p1
FROM [dbo].[Orders] AS j0 INNER JOIN (
SELECT [t0].[OrderID]
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
WHERE [t1].[CustomerID] = @p0
) AS j1 ON (j0.[OrderID] = j1.[OrderID])
-- @p0: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [Bruce]
-- @p1: Input DateTime (Size = 0; Prec = 0; Scale = 0) [2010/8/11 19:51:59]
虽然跟我刚才手写的SQL略有不同,但 Update 的逻辑是对的。有兴趣的朋友不妨试试,Terry Aney在他的文章里有很详尽的介绍,这里不再详述。
相关博文:
Batch Updates and Deletes with LINQ to SQL
LINQ to SQL Batch Updates/Deletes: Fix for 'Could not translate expression'
I've Left Query Analyzer Hell For LINQPad Heaven
总结
Linq To SQL 有很多地方值得探索的,Expression Tree 是探索的基础, 嘿嘿!
完整代码(内含Terry Aney 的代码)
Linq2SQL批量更新.rar
posted @ 2010-07-11 20:49 CoolCode 阅读(1858) | 评论(21) |
编辑

2010年5月26日
目录
.Net4 Expression Tree 入门(1): Hello World!
.Net4 Expression Tree 入门(2): IfThen, SwitchCase
.Net4 Expression Tree 入门(3): Loop 循环
前面已经介绍了.Net4 Expression Tree的分支与循环,是时候综合起来写一个稍微复杂一点的实例了。
求N以内的所有质数
一般的写法
static List<int> GetPrimes_SimpleMethod(int to)
{
var res = new List<int>();
for (int n = 2; n <= to; n++)
{
bool found = false;
for (int d = 2; d <= Math.Sqrt(n); d++)
{
if (n % d == 0)
{
found = true;
break;
}
}
if (!found)
res.Add(n);
}
return res;
}
暂且不用考虑以上算法的时间复杂度、性能什么的,因为只是为了学习 Expression Tree 而已。之前都说过,Expression 里是没有For这些常用方法的,只有Loop,因此为了更方便构造 Expression Tree, 需要对上面代码稍微修改成没for的形式。
如
for (int n = 2; n <= to; n++)
{
// do sth…
}
改成 while形式:
int n = 2;
while (true)
{
if (!(n <= to))
break;
// do sth…
n++;
}
求N以内的所有质数的“NoFor” 写法
【纯粹是为了更方便构造 Expression Tree,否则相信没人用while(true)这样写法】
static List<int> GetPrimes_NoFor(int to)
{
var res = new List<int>();
int n = 2;
while (true)
{
if (!(n <= to))
break;
bool found = false;
int d = 2;
while (true)
{
if (!(d <= Math.Sqrt(n)))
break;
if (n % d == 0)
{
found = true;
break;
}
d++;
}
if (!found)
res.Add(n);
n++;
}
return res;
}
上述代码与表达式的对比列表
|
原始代码 |
表达式 |
| var res = new List<int>(); |
Expression.Assign(
res,
Expression.New(typeof(List<int>))
)
|
| int n = 2; |
Expression.Assign(
n,
Expression.Constant(2)
)
|
while (true)
{
if (!(n <= to))
break;
|
Expression.Loop(
Expression.Block(
Expression.IfThen(
Expression.Not(
Expression.LessThanOrEqual(
n,
to
)
),
Expression.Break(breakOuter)
)
|
| Math.Sqrt(n) |
Expression.Call(
null,
typeof(Math).GetMethod("Sqrt"),
Expression.Convert(
n,
typeof(double)
)
)
|
| d++; |
Expression.PostIncrementAssign(d)
|
| res.Add(n); |
Expression.Call(
res,
typeof(List<int>).GetMethod("Add"),
n
)
|
最终构建表达式的代码很长,有兴趣的不妨自己试着写写,这里不贴上来,可以参考这篇外文 《STATEMENT TREES WITH LESS PAIN – FOLLOW-UP ON SYSTEM.LINQ.EXPRESSIONS V4.0》。
你是否也因为Expression 里没有for这个方法而感到不爽?一切循环都得改成loop这种形式?Expression构建是不是很麻烦罗嗦?为什么Expression一切方法皆是static的…等等。
如果改成下面这种形式会不会好点?
|
原始代码 |
表达式 |
| var res = new List<int>(); |
res.Assign(Expression.New(typeof(List<int>)))
|
| int n = 2; |
n.Assign(2)
|
| n ++; |
n.PostIncrementAssign()
|
| for (int n = 2; n <= to; n++) |
Expression.For(n, n.Assign(2), n <= to, n.PostIncrementAssign()
|
| res.Add(n); |
res.Method("Add", n) |
这样会不会更简洁?欢迎作进一步交流。我会在下一篇随笔里分析如何实现以上形式的写法。
posted @ 2010-05-26 14:59 CoolCode 阅读(1076) | 评论(0) |
编辑
为什么我需要 Expression Tree?
它有什么好处?
实际项目中有可能用到它么?
它可以应用在哪些地方?
… …
以上是很多不认识Expression Tree,或刚接触不久的童鞋们所困惑的问题。实际上,上面这些问题在他们心中只是归纳成一个问题——Expression Tree 值不值得我去学?
于是乎,大多数技术诞生时都会伴随着Showcase,Getting Started,Tutorial,Sample Application等。大学某些导师就是没有得这一窍门,在开课的第一天没有花时间去展示他所教导的课程的Showcase,以至于童鞋们感到very boring,没有动力学下去。关于这一点,当初教导我编程语言的老师经过一段时间后明白了。她在开课前一个月都是讲语法,什么if else for while 啊,发现童鞋们睡倒一大片。于是,她用她所教导的语言编写了几个图形游戏,如打乒乓球之类的。那一堂课,童鞋们专心多了,而我也知道编程不只用于加减乘除了。
Showcase For Expression Tree
严格来说,Expression Tree 是个低调的家伙,以至于它的光芒被其他上层建筑挡住了,如Linq to SQL。因此,也很难找到能让大家眼前一亮的Showcase,而只能限于代码级别去展示。
Case 1: Linq to SQL 动态查询
var queryBuilder = QueryBuilder.Create<Order>()
.Like(c => c.Customer.ContactName, txtCustomer.Text)
.Between(c => c.OrderDate, DateTime.Parse(txtDateFrom.Text), DateTime.Parse(txtDateTo.Text))
.Equals(c => c.EmployeeID, int.Parse(ddlEmployee.SelectedValue))
.In(c => c.ShipCountry, selectedCountries);
详见 QueryBuilder : 打造优雅的Linq To SQL动态查询
更多 Google 一下 Linq to SQL 动态查询
Case 2:打造 Linq Provider
Linq to Google 示例代码
GoogleContext gc = new GoogleContext(key);
var r = from ipods in gc.products
where ipods.BaseQuery == "mp3 players" && ipods.Brand == "apple" && ipods.FeedType == "snippets" && ipods.Price < 200
orderby ipods.Price
select new { ipods.Title, ipods.Price };
更多 Walkthrough: Creating an IQueryable LINQ Provider
Google 一下 Linq Provider
Case 3:动态代码、动态编译、动态组件
var c = new CodeDomGenerator();
c.AddNamespace("Samples").AddClass("TestClass")
.AddMethod(MemberAttributes.Public | MemberAttributes.Static, () => "Print",
Emit.stmt(() => Console.WriteLine("Hello, world!"))
);
Console.WriteLine(c.GenerateCode(CodeDomGenerator.Language.CSharp));
Console.WriteLine(c.GenerateCode(CodeDomGenerator.Language.VB));
Assembly assembly = c.Compile();
详看Expressions to CodeDOM
以上示例代码都见不到 Expression Tree 的影子,因为 Expression Tree 是支撑物,默默地成为伟大建筑的一根支柱。
以上示例代码并不是说非用 Expression Tree 不可,底层实现方法有很多,Expression Tree 只是其中一种。
结论
Expression Tree 永远不会孤军作战的, 正如一根支柱不会独立存在那样。
posted @ 2010-05-26 12:14 CoolCode 阅读(1953) | 评论(16) |
编辑

2010年5月17日
前言
本来开篇应该介绍什么是 Expression Tree,以及为什么需要 Expression Tree,或者.Net4 关于 Expression Tree 的改进等等,但其实我对于Expression Tree的理解也是来自于google,你不妨 google一下 获取更多更新的介绍。
目录
Expression Tree 值不值得我去学?
.Net4 Expression Tree 入门(1): Hello World!
.Net4 Expression Tree 入门(2): IfThen, SwitchCase
.Net4 Expression Tree 入门(3): Loop 循环
.Net4 Expression Tree 入门(4): 求N以内的所有质数
(待续...)
源代码
CoolCode.ExpressionTreeSample.zip
posted @ 2010-05-17 00:39 CoolCode 阅读(1178) | 评论(4) |
编辑
上一篇的 Hello World 简单介绍了.Net4 Expression Tree 的用法,这一篇主要介绍以下表达式——
public static ConditionalExpression IfThen(Expression test, Expression ifTrue);
public static ConditionalExpression IfThenElse(Expression test, Expression ifTrue, Expression ifFalse);
public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, params SwitchCase[] cases);
public static SwitchCase SwitchCase(Expression body, params Expression[] testValues);
没错,都是条件语句。相信很多学编程的童鞋们学完 Hello World 以后,就会接着学if、switch 之类的语句。而表达式也同样如此。虽然形式上表达式跟编程语言有点类似,但实际使用中却没那么友好,呵呵!
IfThen
要实现的语句:
if (score < 60)
{
Console.WriteLine("不合格!");
}
对应的表达式:
var ifThenExpr = Expression.IfThen(
Expression.LessThan(scoreExpr, Expression.Constant(60)),// score < 60
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("不合格!")
)// Console.WriteLine("不合格!");
);
IfThenElse
要实现的语句:
if (score < 60)
{
Console.WriteLine("不合格!");
}
else
{
Console.WriteLine("合格!");
}
对应的表达式:
var ifThenElseExpr = Expression.IfThenElse(
Expression.LessThan(scoreExpr, Expression.Constant(60)),// score < 60
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("不合格!")
),// Console.WriteLine("不合格!");
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("合格!")
)// Console.WriteLine("合格!");
);
SwitchCase
要实现的语句:
switch (n)
{
case 1:
Console.WriteLine("一言九鼎!");
break;
case 2:
Console.WriteLine("双喜临门!");
break;
case 3:
Console.WriteLine("三生有幸!");
break;
case 4:
Console.WriteLine("四海为家!");
break;
default:
Console.WriteLine("Unknown!");
break;
}
对应的表达式:
var switchCaseExpr = Expression.Switch(
//switch (n)
numExpr,
//default: Console.WriteLine("Unknown!?");
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("Unknown!")
),
new SwitchCase[]{
//case 1: Console.WriteLine("一言九鼎!");
Expression.SwitchCase(
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("一言九鼎!")
),
Expression.Constant(1)
),
//case 2: Console.WriteLine("双喜临门!");
Expression.SwitchCase(
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("双喜临门!")
),
Expression.Constant(2)
),
//case 3: Console.WriteLine("三生有幸!");
Expression.SwitchCase(
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("三生有幸!")
),
Expression.Constant(3)
),
//case 4: Console.WriteLine("四海为家!");
Expression.SwitchCase(
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }),
Expression.Constant("四海为家!")
),
Expression.Constant(4)
)
}
);
总结
开始两篇的介绍都是很闷的,你可以选择跳过去,不懂再跳回来,或者选择一目十行,但千万别轻言放弃。就如练武之人先练马步,练拳练腿,终将成为一代宗师,再回顾才发现一招一式离不开当初的练习。
posted @ 2010-05-17 00:32 CoolCode 阅读(264) | 评论(2) |
编辑
前言
刚开始学习编程时,我认为程序带来的价值远不如我为它写的代码多,因为它顶多算加减乘除比一般人快而已,直到我认识了“循环”。而一天后,我又发现程序可以“死循环”,而当时我应付“死循环”的方法是——关机 (- . -!) 狂汗啊!
Loop 循环
这篇文章介绍 Expression Tree 的循环语句:For,Do,While。咳咳,应该是Loop!没错,Expreession Tree里没有For,Do,While这些熟悉的朋友,叫了他们的祖先Loop来应付。正如鲁迅那句经典的语录——世界上本没有For,Do,While,程序员写代码写多了烦了,也便有了。
实际上,Loop和Do差不多,它等价于
do{
if(不合适)
break;
}
以下语句可能会有助于你对上述伪代码的理解【未成年的童鞋直接跳到下一段】:不管三七二十一,先do了再说,如果性格不合适就分手。由此可以判断:Loop不是一个专一的家伙。
示例
现在举一个阶乘(如: n!=n*(n-1)*…*2*1)的例子来说明Expreession Tree是如何做循环的。
一般来说,用C#语法是这样写的:
int result = 1;
do
{
result *= n;
n--;
} while (n > 1);
为了更接近方便构建Expreession Tree,把以上语句改成伪代码格式:
int result = 1;
do
{
if (n > 1)
{
result *= n;
n--;
}
else
{
break;
}
}
对应的表达式树构建:
ParameterExpression value = Expression.Parameter(typeof(int), "value");
ParameterExpression result = Expression.Parameter(typeof(int), "result");
LabelTarget label = Expression.Label(typeof(int));
BlockExpression block = Expression.Block(
new[] { result },
Expression.Assign(result, Expression.Constant(1)),
Expression.Loop(
Expression.IfThenElse(
Expression.GreaterThan(value,
Expression.Constant(1)),
Expression.MultiplyAssign(result,
Expression.PostDecrementAssign(value)),
Expression.Break(label, result)
),
label
)
);
Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(block, value);
Func<int, int> func = lambda.Compile();
Console.WriteLine(func(5));
一般来说,Expression Tree 的循环由Expression.Loop 、 Expression.Break 和 LabelTarget 共同构成,如下面格式:
Expression.Loop(
…
Expression.Break(label)
…,
label
)
为了确保跑出“死循环”,必须制定一个条件“跳出去”(如 Break 和 LabelTarget)。
另外, Expression.MultiplyAssign(result, Expression.PostDecrementAssign(value)) 所表达的就是result *= n--; 也就是 result *= n; n—; 合并成一个语句而已。
总结
值得思考 Expression 里为什么没有For这样常用的方法?
这里埋下伏笔,其实我很早就写好了For,在我自定义的FluentExpression里的写法大概是这样子,以后会详细介绍。
FluentExpression.For(n, n.Assign(2), n <= to, n.PostIncrementAssign() …)
posted @ 2010-05-17 00:29 CoolCode 阅读(234) | 评论(0) |
编辑
某程序员退休后决定练习书法,
于是重金购买文房四宝。
一日,饭后突生雅兴,一番研墨拟纸,并点上上好檀香。
定神片刻,泼墨挥毫,
郑重地写下一行字:Hello World!
回忆大学学习.Net时,写下的第一行代码是:
Console.WriteLine("Hello World!");
同样,作为.Net4 Expression Tree 入门示例,我认为经典的“Hello World“ 应该是最合适不过了。
本篇为了更好地演示.Net4 Expression Tree 的新功能,特意把 Hello World 的代码稍微修改一下:
string s;
s = "Hello World!";
Console.WriteLine(s);
然后用表达式逐一实现以上3条语句。
1. string s;
var s = Expression.Variable(typeof(string), "s");
Expression.Variable:声明一个变量。第一个参数指定类型,第二个参数指定名字(可选)。
2. s = “Hello World”;
var assignExpression = Expression.Assign(s, Expression.Constant("Hello World!"));
Expression.Assign:对s赋值
3. Console.WriteLine(s);
var callExpression = Expression.Call(null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }), // Console.WriteLine(s);
s
);
Expression.Call:调用Console.WriteLine方法
最后,需要 Expression.Block 把以上三个表达式组织起来:
Expression.Block(
new[] { s },
assignExpression,
callExpression)
以下是 Expression.Block 的其中一个函数签名:
public static BlockExpression Block(IEnumerable<ParameterExpression> variables, params Expression[] expressions);
第一个参数是指定变量(如示例的s),第二个参数是具体实现的表达式(如赋值、循环等等)。
完整的代码:
var s = Expression.Variable(typeof(string),"s");//string s;
var assignExpression = Expression.Assign(s, Expression.Constant("Hello World!"));//s = "Hello World!"
var callExpression = Expression.Call(null,
typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }), // Console.WriteLine(s);
s
);
var writelineExpression = Expression.Lambda<Action>(Expression.Block(
new[] { s },
assignExpression,
callExpression)
);
var writeline = writelineExpression.Compile();
writeline();
总结
Hello,Expression Tree!
posted @ 2010-05-17 00:07 CoolCode 阅读(536) | 评论(4) |
编辑

2010年4月1日
摘要: 今天,2010年4月1日我拿到了一份相对满意的offer(当然对于我来说,永远没有非常满意的offer)。 想当年,也是4月1日,还没大学毕业的我进入了一家港资公司做ERP开发。现在依然记忆犹新,那天我看了一天的文档(这全英文档比牛津字典更牛更厚)。下班了,还扛着这些文档回到宿舍继续看。永远记得那天,下着蒙蒙细雨,我对自己说:N年之后,你要成为一位成功的技术牛人。 结束了十天的“面霸”生活,感觉生...
阅读全文
posted @ 2010-04-01 20:46 CoolCode 阅读(2722) | 评论(18) |
编辑